| Viettel Global Investment JSC was founded in 2007 by Viettel Group, a leading digital service provider based in Vietnam with over 50% market share in the telecom industry. This project will estimate and evaluate the risk and return associated with its stock (VGI) from 2019 - 2021 through 2 measures: CAPM Beta and Sharpe Ratio. |
| Time-series data source: Yahoo Finance, Investing.com |
Beta of stock is a widely used measure of its risk and volatility. Moreover, this measure captures the sensitivity of a security to market fluctuations. A beta greater than 1.0 suggests that the stock is more volatile than the broader market, and a beta less than 1.0 indicates a stock with lower volatility.
Estimation Method: Ordinary Least Squares (OLS) regression
\(𝑅i,t =𝛼+𝛽i𝑅m,t +𝜀\)
𝑅i,t is the return on stock i, at time t. 𝛽! is Beta of stock i
𝑅m,t is return on index m, at time t.
𝛼 is the intercept and 𝜀 is the error term
Vaneck Vectors Vietnam ETF (VNM)’s returns are used as a benchmark for market performance. By regressing VGI’s rate of return (RoR) on VNM’s RoR, we will get the associated beta coefficient as a measure of volatility.
1. First, load the packaged requires to wrangle/visualize data
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5 ✓ purrr 0.3.4
## ✓ tibble 3.1.5 ✓ stringr 1.4.0
## ✓ tidyr 1.1.4 ✓ forcats 0.5.1
## ✓ readr 2.1.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(ggplot2)
library(cowplot)
library(plotly)
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(xts)
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
##
## Attaching package: 'xts'
## The following objects are masked from 'package:dplyr':
##
## first, last
library(dygraphs)
2. Load stock price datasets
#VGI Stock Price
vgi <- read.csv("~/Documents/Data Science /R/projects/vgi.csv")
#VNM Index
vnm <- read.csv("~/Documents/Data Science /R/projects/VNM.csv")
3. Merge datasets into one to run regression / plot data
#inner join, foreign key Date
merged_dt <- inner_join(vgi, vnm, by = "Date")
dim(merged_dt)
## [1] 832 11
#rename variables
revised_names <- c("Date", "VGI_Close", "VGI_Open", "VGI_High", "VGI_Low", "VGI_Vol",
"VNM_Open", "VNM_High", "VNM_Low", "VNM_Close", "VNM_Vol")
colnames(merged_dt) <- revised_names
head(merged_dt)
## Date VGI_Close VGI_Open VGI_High VGI_Low VGI_Vol VNM_Open VNM_High
## 1 2018-09-26 23900 23900 23900 23800 214.20K 16.93 16.93
## 2 2018-09-27 27400 27400 27400 27400 388.10K 16.80 16.89
## 3 2018-09-28 28200 31500 31500 27400 2.11M 16.84 16.90
## 4 2018-10-01 26800 29500 29600 25800 831.10K 16.80 16.81
## 5 2018-10-02 26000 27100 27300 25900 596.27K 16.55 16.72
## 6 2018-10-03 23500 26200 26200 23000 705.23K 16.74 16.75
## VNM_Low VNM_Close VNM_Vol
## 1 16.73 16.37579 281900
## 2 16.79 16.40505 401300
## 3 16.73 16.39529 257300
## 4 16.55 16.17097 378400
## 5 16.53 16.16121 190000
## 6 16.47 16.08319 86900
#change VNM unit price to VND
merged_dt$VNM_Close <- merged_dt$VNM_Close*22840
4. We are now ready to calculate the rate of returns of VGI and VNM ETF
#Calculate VGI RoR
n = nrow(merged_dt)
merged_dt$VGI_RoR[2:n] <- ((merged_dt$VGI_Close[2:n] - merged_dt$VGI_Close[1:(n-1)])/merged_dt$VGI_Close[1:(n-1)] * 100)
merged_dt$VGI_RoR[1] <- 0
#Calculate VNM RoR
merged_dt$VNM_RoR[2:n] <- ((merged_dt$VNM_Close[2:n] - merged_dt$VNM_Close[1:(n-1)])/merged_dt$VNM_Close[1:(n-1)] * 100)
merged_dt$VNM_RoR[1] <- 0
#double check
head(merged_dt)
## Date VGI_Close VGI_Open VGI_High VGI_Low VGI_Vol VNM_Open VNM_High
## 1 2018-09-26 23900 23900 23900 23800 214.20K 16.93 16.93
## 2 2018-09-27 27400 27400 27400 27400 388.10K 16.80 16.89
## 3 2018-09-28 28200 31500 31500 27400 2.11M 16.84 16.90
## 4 2018-10-01 26800 29500 29600 25800 831.10K 16.80 16.81
## 5 2018-10-02 26000 27100 27300 25900 596.27K 16.55 16.72
## 6 2018-10-03 23500 26200 26200 23000 705.23K 16.74 16.75
## VNM_Low VNM_Close VNM_Vol VGI_RoR VNM_RoR
## 1 16.73 374023.0 281900 0.000000 0.00000000
## 2 16.79 374691.3 401300 14.644351 0.17867842
## 3 16.73 374468.5 257300 2.919708 -0.05946950
## 4 16.55 369344.9 378400 -4.964539 -1.36821595
## 5 16.53 369122.2 190000 -2.985075 -0.06031797
## 6 16.47 367340.1 86900 -9.615385 -0.48278548
5. To better visualize the rate of returns, we will compare their distributions through the histograms below:
#VGI returns histogram
hist_vgi <- ggplot(merged_dt, aes(VGI_RoR)) +
geom_histogram(fill = "brown", color = "black") +
labs(title = "Viettel Global (VGI)", subtitle = "3-year period", x = "Daily returns (%)", y = "Count (days)") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(hist_vgi)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#Market returns histogram
hist_3yr <- ggplot(merged_dt, aes(VNM_RoR)) +
geom_histogram(fill = "orange", color = "black") +
labs(title = "Market (VNM)", subtitle = "3-year period", x = "Daily returns (%) ", y = "Count (days)") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(hist_3yr)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#Market returns vs VGI returns, side by side, 3 year period
plot_grid(hist_3yr, hist_vgi)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
6. We will now run a simple linear regression model, the beta coefficient of which will be the VGI’s stock beta for the 3-year period from late 2018 until Feb 2022.
vgi_vnm <- lm(merged_dt$VGI_RoR ~ merged_dt$VNM_RoR)
summary(vgi_vnm)
##
## Call:
## lm(formula = merged_dt$VGI_RoR ~ merged_dt$VNM_RoR)
##
## Residuals:
## Min 1Q Median 3Q Max
## -10.7246 -1.5231 -0.3399 1.1527 14.9364
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.06058 0.10337 0.586 0.558
## merged_dt$VNM_RoR 0.73995 0.06867 10.776 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.981 on 830 degrees of freedom
## Multiple R-squared: 0.1227, Adjusted R-squared: 0.1217
## F-statistic: 116.1 on 1 and 830 DF, p-value: < 2.2e-16
From the model report, VGI’s beta is 0.73995, indicating a relatively low risk for the past 3 years.
plot1 <- ggplot(merged_dt, aes(VNM_RoR, VGI_RoR)) +
geom_point(position = "jitter", alpha = 0.5, color = "tomato3") +
labs(title = "RoR: VGI vs VNM", subtitle = "3-year beta: 0.74", x = "VNM daily returns (%)", y = "VGI daily returns (%)") +
geom_smooth(method = "lm", se = FALSE, col = "black") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(plot1)
## `geom_smooth()` using formula 'y ~ x'
7. Period Breakdown Analysis
We will now estimate the beta for Viettel Global Investment (VGI) in 3 consecutive time periods: 2019, 2020, 2021.
a. 2019 period
#filter for 2019 data
dt_2019 <- filter(merged_dt, grepl("2019", Date, ignore.case = TRUE))
head(dt_2019)
## Date VGI_Close VGI_Open VGI_High VGI_Low VGI_Vol VNM_Open VNM_High
## 1 2019-01-02 13200 13200 14000 13100 60.56K 14.69 15.11
## 2 2019-01-03 13000 13300 13400 12800 74.90K 14.76 14.83
## 3 2019-01-04 12600 12700 13000 12600 60.45K 14.64 14.92
## 4 2019-01-07 13000 12700 13400 12400 65.70K 14.94 15.09
## 5 2019-01-08 13100 13000 13500 13000 42.26K 15.02 15.05
## 6 2019-01-09 13300 13000 13500 13000 84.64K 15.04 15.21
## VNM_Low VNM_Close VNM_Vol VGI_RoR VNM_RoR
## 1 14.65 334654.9 267900 -1.4925373 0.3366916
## 2 14.40 325221.7 203300 -1.5151515 -2.8187959
## 3 14.61 334879.6 187600 -3.0769231 2.9696198
## 4 14.84 337799.4 264400 3.1746032 0.8719015
## 5 14.81 336002.6 120900 0.7692308 -0.5319201
## 6 15.02 340270.0 149100 1.5267176 1.2700575
RoR Distribution:
#VGI returns histogram: 2019
hist_vgi_19 <- ggplot(dt_2019, aes(VGI_RoR)) +
geom_histogram(fill = "brown", color = "black") +
labs(title = "Viettel Global (VGI)", subtitle = "2019", x = "Daily returns (%)", y = "Frequency") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(hist_vgi_19)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#Market returns histogram: 2019
hist_vnm_19 <- ggplot(dt_2019, aes(VNM_RoR)) +
geom_histogram(fill = "orange", color = "black") +
labs(title = "Market (VNM)", subtitle = "2019", x = "Daily returns (%) ", y = "Frequency") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(hist_vnm_19)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
#Market returns vs VGI returns, side by side, 2019
plot_grid(hist_vnm_19, hist_vgi_19)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Regression + Beta:
#2019 beta
beta_19 <- lm(dt_2019$VGI_RoR ~ dt_2019$VNM_RoR)
summary(beta_19)
##
## Call:
## lm(formula = dt_2019$VGI_RoR ~ dt_2019$VNM_RoR)
##
## Residuals:
## Min 1Q Median 3Q Max
## -11.2914 -1.7881 -0.3519 1.2842 14.9333
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.30345 0.21480 1.413 0.159
## dt_2019$VNM_RoR 0.07332 0.21346 0.344 0.732
##
## Residual standard error: 3.346 on 241 degrees of freedom
## Multiple R-squared: 0.0004894, Adjusted R-squared: -0.003658
## F-statistic: 0.118 on 1 and 241 DF, p-value: 0.7315
From the model report, VGI’s beta is 0.07332, signifying very low sensitivity to market overall performance. R-squared is low at -0.003658, indicating variation of the response variable VGI is not at all explained by the model. The statistically insignificant p-value (0.7315 > 0.05) further proves that there is little to no association between VGI stock price and overall market performance. In other words, in 2019, VGI stock price is not at all influenced by market sentiment.
Visualize:
plot_2019 <- ggplot(dt_2019, aes(VNM_RoR, VGI_RoR)) +
geom_point(position = "jitter", alpha = 0.5, color = "coral1") +
labs(title = "RoR: VGI vs VNM", subtitle = "2019 beta: 0.07", x = "VNM daily returns (%)", y = "VGI daily returns (%)") +
geom_smooth(method = "lm", se = FALSE, col = "black") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(plot_2019)
## `geom_smooth()` using formula 'y ~ x'
The almost horizontal regression line indicates very low correlation between VGI’s stock price and ETF. 2019 was a year of low risk for Viettel Global Investment JSC.
b. 2020 period
#filter for 2020 data
dt_2020 <- filter(merged_dt, grepl("2020", Date, ignore.case = TRUE))
head(dt_2020)
## Date VGI_Close VGI_Open VGI_High VGI_Low VGI_Vol VNM_Open VNM_High
## 1 2020-01-02 25300 25000 25300 24700 152.89K 16.00 16.27
## 2 2020-01-03 25000 25400 25400 24800 130.71K 16.20 16.20
## 3 2020-01-06 24600 24900 25000 24400 164.57K 16.00 16.07
## 4 2020-01-07 24500 24700 24700 24300 124.00K 16.04 16.09
## 5 2020-01-08 23200 24200 24300 23000 505.00K 15.85 15.92
## 6 2020-01-09 23200 23400 23700 22900 157.15K 15.86 15.92
## VNM_Low VNM_Close VNM_Vol VGI_RoR VNM_RoR
## 1 15.99 368002.5 615400 2.4291498 1.68857039
## 2 16.00 364155.0 402400 -1.1857708 -1.04551249
## 3 15.90 363023.4 157600 -1.6000000 -0.31075521
## 4 16.01 362344.4 108500 -0.4065041 -0.18703686
## 5 15.77 358949.5 193300 -5.3061224 -0.93691782
## 6 15.80 358723.2 220100 0.0000000 -0.06305108
RoR Distribution:
#VGI returns histogram: 2020
hist_vgi_20 <- ggplot(dt_2020, aes(VGI_RoR)) +
geom_histogram(fill = "brown", color = "black") +
labs(title = "Viettel Global (VGI)", subtitle = "2020", x = "Daily returns (%)", y = "Frequency") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
#Market returns histogram: 2020
hist_vnm_20 <- ggplot(dt_2020, aes(VNM_RoR)) +
geom_histogram(fill = "orange", color = "black") +
labs(title = "Market (VNM)", subtitle = "2020", x = "Daily returns (%) ", y = "Frequency") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
#Market returns vs VGI returns, side by side, 2020
plot_grid(hist_vnm_20, hist_vgi_20)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Regression + Beta:
beta_20 <- lm(dt_2020$VGI_RoR ~ dt_2020$VNM_RoR)
summary(beta_20)
##
## Call:
## lm(formula = dt_2020$VGI_RoR ~ dt_2020$VNM_RoR)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.4312 -1.3459 -0.1398 1.0471 12.7268
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.12605 0.17249 0.731 0.466
## dt_2020$VNM_RoR 0.83247 0.08177 10.180 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.693 on 242 degrees of freedom
## Multiple R-squared: 0.2998, Adjusted R-squared: 0.297
## F-statistic: 103.6 on 1 and 242 DF, p-value: < 2.2e-16
From the model report, VGI’s beta in 2020 is 0.83247, indicating higher and more prominent risk from the year before. However, as beta here is < 1, it is still considered relatively safe compared to other stocks in the market (with beta > 1). This was due mostly to the onset of the COVID-19 pandemic leading to national lock-down and business closure.
Visualize:
plot_2020 <- ggplot(dt_2020, aes(VNM_RoR, VGI_RoR)) +
geom_point(position = "jitter", alpha = 0.5, color = "brown3") +
labs(title = "RoR: VGI vs VNM", subtitle = "2020 beta: 0.83", x = "VNM daily returns (%)", y = "VGI daily returns (%)") +
geom_smooth(method = "lm", se = FALSE, col = "black") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(plot_2020)
## `geom_smooth()` using formula 'y ~ x'
c. 2021 period
#filter for 2021 data
dt_2021 <- filter(merged_dt, grepl("2021", Date, ignore.case = TRUE))
head(dt_2021)
## Date VGI_Close VGI_Open VGI_High VGI_Low VGI_Vol VNM_Open VNM_High
## 1 2021-01-04 34600 33300 35000 33300 1.23M 17.60 17.79
## 2 2021-01-05 34400 34600 34700 34000 578.13K 17.75 17.99
## 3 2021-01-06 34800 34300 35800 34300 1.07M 17.96 18.05
## 4 2021-01-07 35000 34800 35100 34300 1.04M 18.07 18.22
## 5 2021-01-08 38000 35000 38300 34900 3.36M 18.25 18.46
## 6 2021-01-11 41700 38000 41800 38000 1.44M 18.48 18.58
## VNM_Low VNM_Close VNM_Vol VGI_RoR VNM_RoR
## 1 17.53 398854.2 580500 3.2835821 0.3430505
## 2 17.73 408172.2 502900 -0.5780347 2.3361778
## 3 17.83 409081.3 641100 1.1627907 0.2227248
## 4 18.01 413853.9 813100 0.5747126 1.1666633
## 5 18.21 418626.5 499900 8.5714286 1.1532147
## 6 18.42 420671.9 410600 9.7368421 0.4886010
RoR Distribution:
#VGI returns histogram: 2020
hist_vgi_21 <- ggplot(dt_2021, aes(VGI_RoR)) +
geom_histogram(fill = "brown", color = "black") +
labs(title = "Viettel Global (VGI)", subtitle = "2021", x = "Daily returns (%)", y = "Frequency") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
#Market returns histogram: 2020
hist_vnm_21 <- ggplot(dt_2021, aes(VNM_RoR)) +
geom_histogram(fill = "orange", color = "black") +
labs(title = "Market (VNM)", subtitle = "2021", x = "Daily returns (%) ", y = "Frequency") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
#Market returns vs VGI returns, side by side, 2020
plot_grid(hist_vnm_21, hist_vgi_21)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Regression + Beta:
#2021 beta
beta_21 <- lm(dt_2021$VGI_RoR ~ dt_2021$VNM_RoR)
summary(beta_21)
##
## Call:
## lm(formula = dt_2021$VGI_RoR ~ dt_2021$VNM_RoR)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.5115 -1.4673 -0.3321 1.2286 11.6596
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.05591 0.17103 -0.327 0.744
## dt_2021$VNM_RoR 0.98458 0.14545 6.769 9.78e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.658 on 241 degrees of freedom
## Multiple R-squared: 0.1598, Adjusted R-squared: 0.1563
## F-statistic: 45.82 on 1 and 241 DF, p-value: 9.775e-11
#==> 2021 beta = 0.98458
In 2021, VGI beta went very close to 1 at 0.98, indicating an increase in volatility influenced by market performance. This roughly tells that as the market price move by 1 unit, VGI stock price moves by 0.98 unit.
Visualize:
plot_2021 <- ggplot(dt_2021, aes(VNM_RoR, VGI_RoR)) +
geom_point(position = "jitter", alpha = 0.5, color = "brown1") +
labs(title = "RoR: VGI vs VNM", subtitle = "2021 beta: 0.98", x = "VNM daily returns (%)", y = "VGI daily returns (%)") +
geom_smooth(method = "lm", se = FALSE, col = "black") +
theme_classic() +
theme(plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), text = element_text(family = "serif"))
ggplotly(plot_2021)
## `geom_smooth()` using formula 'y ~ x'
d. Overall Comparison
plot_grid(plot1, plot_2019, plot_2020, plot_2021)
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
It can be observed that beta of Viettel Global Investment (VGI) increases as time progresses. As events with significant impact make their marks on the global world, Vietnamese economy is also undergoing substantial changes, which are likely impacting VGI’s stock price in an escalating manner.
Sharpe ratio is one of the most popular risk-reward measures in finance. This ratio tells how much more return for a given unit of risk a stock would generate relative to just putting money into a low-cost index fund. For this measure, we will execute a peer comparison between VGI (Viettel Global) and SGT (Saigon Telecommunication & Technologies Corp).
Annualized Sharpe Ratio = Average Return of Difference / Standard Deviation of Return Difference * Annual Factor
Based on the above formula, we will calculate annualized Sharpe Ratio for the stocks of two telecom companies Viettel and SGT, using Vaneck Vietnam ETF Fund (VNM) as a banchmark.
1. Load dataset + convert into xts objects to plot time series
#Load SGT dataset
sgt <- read.csv("~/Documents/Data Science /R/projects/SGT Historical Data-2.csv")
#Convert vgi into an xts object
vgi_ts <- xts(vgi[,2], order.by=as.POSIXct(vgi$Date))
#Convert sgt into an xts object
sgt_ts <- xts(sgt[,2], order.by=as.POSIXct(sgt$Date))
#Convert vnm into an xts object
vnm_ts <- xts(vnm[,5], order.by=as.POSIXct(vnm$Date))
2. Merge and filter datasets
merged_dt <- inner_join(vgi, vnm, by = "Date")
#merged data
vgi_sgt_vnm <- inner_join(merged_dt, sgt, by = "Date")
revised_names <- c("Date", "VGI_Close", "VGI_Open", "VGI_High", "VGI_Low", "VGI_Vol",
"VNM_Open", "VNM_High", "VNM_Low", "VNM_Close", "VNM_Vol", "SGT_Close", "SGT_Open", "SGT_High", "SGT_Low", "SGT_Vol")
colnames(vgi_sgt_vnm) <- revised_names
vgi_sgt_vnm$VNM_Close <- vgi_sgt_vnm$VNM_Close*22840
#select only closing prices
concise_dt <- vgi_sgt_vnm %>%
select(Date, VGI_Close, SGT_Close, VNM_Close)
#convert to xts for plotting
concise_dt <- xts(concise_dt[,2:4], order.by=as.POSIXct(concise_dt$Date))
head(concise_dt)
## VGI_Close SGT_Close VNM_Close
## 2018-09-26 23900 5230 374023.0
## 2018-09-27 27400 5490 374691.3
## 2018-09-28 28200 5400 374468.5
## 2018-10-01 26800 5030 369344.9
## 2018-10-02 26000 4750 369122.2
## 2018-10-03 23500 4700 367340.1
3. Price movement observation
Plot daily price movement for VGI and SGT:
#Individual plots
vgi_plot <- dygraph(vgi_ts, main = "VGI Stock Price", ylab = "VND") %>%
dyOptions(fillGraph = TRUE, drawGrid = FALSE, colors = "brown")
vgi_plot
sgt_plot <- dygraph(sgt_ts, main = "SGT Stock Price", ylab = "VND") %>%
dyOptions(fillGraph = TRUE, drawGrid = FALSE, colors = "steelblue")
sgt_plot
#Together in a plot
agg_plot <- dygraph(concise_dt[, 1:2], main = "VGI vs SGT", ylab = "VND") %>%
dySeries("VGI_Close", label = "VGI", color = "brown", fillGraph = "brown") %>%
dySeries("SGT_Close", label = "SGT", color = "steelblue", fillGraph = "steelblue") %>%
dyOptions(drawGrid = FALSE, axisLabelFontSize = 11, fillAlpha = 0.1)
agg_plot
Plot benchmark price movement:
vnm_plot <- dygraph(concise_dt[,3], main = "VNM Stock Index", ylab = "VND") %>%
dyOptions(drawGrid = FALSE, colors = "orange", axisLabelFontSize = 11, fillAlpha = 0.1, fillGraph = TRUE)
vnm_plot
4. Daily Stock Returns (RoR)
Calculation:
#Calculate VGI RoR
n = nrow(vgi_sgt_vnm)
vgi_sgt_vnm$VGI_RoR[2:n] <- ((vgi_sgt_vnm$`VGI_Close`[2:n] - vgi_sgt_vnm$`VGI_Close`[1:(n-1)])/vgi_sgt_vnm$`VGI_Close`[1:(n-1)] * 100)
vgi_sgt_vnm$VGI_RoR[1] <- 0
#Calculate SGT RoR
vgi_sgt_vnm$SGT_RoR[2:n] <- ((vgi_sgt_vnm$`SGT_Close`[2:n] - vgi_sgt_vnm$`SGT_Close`[1:(n-1)])/vgi_sgt_vnm$`SGT_Close`[1:(n-1)] * 100)
vgi_sgt_vnm$SGT_RoR[1] <- 0
#Calculate VNM RoR
vgi_sgt_vnm$VNM_RoR[2:n] <- ((vgi_sgt_vnm$`VNM_Close`[2:n] - vgi_sgt_vnm$`VNM_Close`[1:(n-1)])/vgi_sgt_vnm$`VNM_Close`[1:(n-1)] * 100)
vgi_sgt_vnm$VNM_RoR[1] <- 0
ror_dt <- xts(vgi_sgt_vnm[,17:19], order.by=as.POSIXct(vgi_sgt_vnm$Date))
head(ror_dt)
## VGI_RoR SGT_RoR VNM_RoR
## 2018-09-26 0.000000 0.000000 0.00000000
## 2018-09-27 14.644351 4.971319 0.17867842
## 2018-09-28 2.919708 -1.639344 -0.05946950
## 2018-10-01 -4.964539 -6.851852 -1.36821595
## 2018-10-02 -2.985075 -5.566600 -0.06031797
## 2018-10-03 -9.615385 -1.052632 -0.48278548
Plot daily stock returns:
stock_returns <- ror_dt[, 1:2]
st_returns <- dygraph(stock_returns, main = "Stock Daily Returns", ylab = "Percent (%)") %>%
dySeries("VGI_RoR", label = "VGI", color = "brown") %>%
dySeries("SGT_RoR", label = "SGT", color = "steelblue") %>%
dyOptions(drawGrid = FALSE, axisLabelFontSize = 11, drawAxesAtZero = TRUE)
st_returns
Plot daily benchmark returns:
benchmark_rt <- dygraph(ror_dt[,3], main = "Benchmark Daily Returns", ylab = "Percent (%)") %>%
dyOptions(colors = "orange", drawGrid = FALSE, axisLabelFontSize = 12, drawAxesAtZero = TRUE)
benchmark_rt
5. Excess Returns
This measures the relative performance of the two stocks versus VNM ETF benchmark. Excess returns = stock_returns - benchmark_rt (daily).
ror_dt$excess_rt_vgi <- ror_dt[, 1] - ror_dt[,3]
ror_dt$excess_rt_sgt <- ror_dt[, 2] - ror_dt[,3]
Visualize:
stock_excess <- dygraph(ror_dt[,4:5], main = "Stock Excess Returns", ylab = "Percent (%)") %>%
dySeries("excess_rt_vgi", label = "VGI", color = "brown") %>%
dySeries("excess_rt_sgt", label = "SGT", color = "steelblue") %>%
dyOptions(drawGrid = FALSE, axisLabelFontSize = 12, drawAxesAtZero = TRUE)
stock_excess
6. Average of Difference in Stock Returns
avg_exrt_vgi <- mean(ror_dt$excess_rt_vgi)
avg_exrt_sgt <- mean(ror_dt$excess_rt_sgt)
Visualize:
#Create dataframe for plotting
a1 <- c("Viettel Global Investment (VGI)", "Saigon Telecommunication (SGT)")
a2 <- c(avg_exrt_vgi, avg_exrt_sgt)
avg_re <- data.frame(a1, a2)
colnames(avg_re) <- c("Company", "avg_return_diff")
avg_re
## Company avg_return_diff
## 1 Viettel Global Investment (VGI) 0.0546441
## 2 Saigon Telecommunication (SGT) 0.2758362
#Compare avg differences in returns
plot_avg <- ggplot(avg_re, aes(Company, avg_return_diff, fill = Company)) +
geom_bar(stat = "identity") +
labs(title = "Average of Return Difference", subtitle = "SGT has significantly higher average returns than VGI", y = "Average (Unit)", x = "") +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"), plot.subtitle = element_text(hjust = 0.5, size = 10), text = element_text(family = "serif"), panel.grid.major.x = element_blank()) +
scale_fill_manual(labels = c("SGT", "VGI"), values = c("steelblue", "brown"))
plot_avg
7. Standard Deviation of Difference in Stock Returns
The standard deviation of excess_returns shows us the amount of risk an investment in the stocks implies as compared to an investment in the ETF fund.
sd_exrt_vgi <- sd(ror_dt$excess_rt_vgi)
sd_exrt_sgt <- sd(ror_dt$excess_rt_sgt)
Visualize:
#Create dataframe
r1 <- c("Viettel Global Investment (VGI)", "Saigon Telecommunication (SGT)")
r2 <- c(sd_exrt_vgi, sd_exrt_sgt)
risk_sd <- data.frame(r1, r2)
colnames(risk_sd) <- c("Company", "sd_return_diff")
risk_sd
## Company sd_return_diff
## 1 Viettel Global Investment (VGI) 3.030645
## 2 Saigon Telecommunication (SGT) 4.407414
#Compare std differences in returns
plot_risk <- ggplot(risk_sd, aes(Company, sd_return_diff, fill = Company)) +
geom_bar(stat = "identity") +
labs(title = "Standard Deviation of Return Difference", subtitle = "SGT is risker than VGI by 1 unit", y = "Standard Deviation (Unit)", x = "") +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"), plot.subtitle = element_text(hjust = 0.5, size = 10), text = element_text(family = "serif"), panel.grid.major.x = element_blank()) +
scale_fill_manual(labels = c("SGT", "VGI"), values = c("steelblue", "brown"))
plot_risk
8. Sharpe Ratio Calculation: Final Step
#Sharpe Ratio = ratio of avg_excess_returns and sd_excess_returns
vgi_sharpe_ratio <- avg_exrt_vgi / sd_exrt_vgi
sgt_sharpe_ratio <- avg_exrt_sgt / sd_exrt_sgt
#annualized sharpe ratio
annual_factor = sqrt(nrow(ror_dt))
annualized_shratio_vgi <- vgi_sharpe_ratio * annual_factor
annualized_shratio_sgt <- sgt_sharpe_ratio * annual_factor
Visualize + Compare:
#create dataframe
c1 <- c("Viettel Global Investment (VGI)", "Saigon Telecommunication (SGT)")
c2 <- c(annualized_shratio_vgi, annualized_shratio_sgt)
annual_sratio <- data.frame(c1, c2)
colnames(annual_sratio) <- c("Company", "Sharpe_Ratio")
annual_sratio
## Company Sharpe_Ratio
## 1 Viettel Global Investment (VGI) 0.5048544
## 2 Saigon Telecommunication (SGT) 1.7523684
#Plot annualized sharpe ratios
plot_sratio <- ggplot(annual_sratio, aes(Company, Sharpe_Ratio, fill = Company)) +
geom_bar(stat = "identity") +
labs(title = "Annualized Sharpe Ratio", subtitle = "observed difference in ratio mostly driven by return rather than risk", y = "Sharpe Ratio", x = "") +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"), plot.subtitle = element_text(hjust = 0.5, size = 10), text = element_text(family = "serif"), panel.grid.major.x = element_blank()) +
scale_fill_manual(labels = c("SGT", "VGI"), values = c("steelblue", "brown"))
plot_sratio
From late 2018 to early 2022, SGT had a Sharpe ratio triple that of VGI’s. This means that an investment in SGT returned 3 times as much compared to the VGI for each unit of risk. In other words, in risk-adjusted terms, the investment in SGT would have been more attractive.
This difference was mostly driven by differences in return rather than risk between Viettel Global Investment and Saigon Telecom. The risk of choosing SGT over VGI (as measured by the standard deviation) was only higher by 1 unit whereas the average excess returns of SGI was 3 times that of VGI.Thus, the Sharpe ratio for SGT ends up higher mainly due to its higher average daily returns.
From the above risk-reward measures - Beta and Sharpe Ratio, we can conclude that Viettel Global Investment JSC (ticker: VGI) is a relatively safe investment with an overall beta < 1 and lower risk compared to other peers (i.e. SGT).
Critics argue that beta does not give enough information about the fundamentals of a company, and that beta is probably a better indicator of short-term rather than long-term risk.
The Sharpe ratio has several weaknesses, including an assumption that investment returns are normally distributed. This might not hold true for every stock.