Introduction to estudy2

Iegor Rudnytskyi

2021-11-15

Introduction

The estduy2 package is an implementation of an event study methodology. The event study is a statistical toolbox that allows examining the impact of certain events on the stock valuation of companies. The idea of the event study is to compare the market valuation of companies during an event-free period (called the estimation period) and a period when an event happened (the event period).

In order to complete a full event study one has to:

The estudy2 package provides a set of functions to cover these steps. Functions’ names are self-explanatory and functions should be called consecutively in the following order:

The package is designed to work with one of the data types, namely zoo, list, or data.frame depending on user preference.

The purpose of this vignette is to provide a brief introduction of how to use this package. The illustrative example is the analysis of the impact of the COVID-19 lockdown introduction on seven companies, which could profit from it.

Obtaining prices of companies’ stocks

We select seven companies that could profit from COVID-19 lockdown, tickers of which are listed in the code snippet below. The starting and ending date are set to "2019-04-01" and "2020-04-01", respectively, in order to include both the estimation and the event periods. Then, prices of securities should be downloaded. The function get_prices_from_tickers downloads prices for companies’ tickers between start and end date. Under the hood, this function is a wrapper around get.hist.quote from tseries. The function can return either Open or Close quote (quote argument). Also one has to choose the returning class among "list", "zoo", and "data.frame". Further in the vignette we use zoo objects. We download historical prices of companies with:

library(estudy2)
tickers <- c("AMZN", "ZM", "UBER", "NFLX", "SHOP", "FB", "UPWK")
prices <- get_prices_from_tickers(tickers, 
                                  start = as.Date("2019-04-01"),
                                  end = as.Date("2020-04-01"),
                                  quote = "Close",
                                  retclass = "zoo")

For using a single-index market model we also need to download prices of an index (proxy):

prices_indx <- get_prices_from_tickers("^GSPC",
                                       start = as.Date("2019-04-01"),
                                       end = as.Date("2020-04-01"),
                                       quote = "Close",
                                       retclass = "zoo")

Computing rates of returns from prices

Now we need to compute rates of returns form companies’ prices. For this purpose the function get_rates_from_prices is used, which is an S3 generic function dispatched on prices argument allowing for list, zoo, and data.frame (the same classes which get_prices_from_tickers returns). The function computes rates of returns of given prices using either continuous or discrete compounding depending on compounding argument value. Furthermore, setting multi_day to TRUE allows for rates of returns between non-consecutive days (for instance, Friday - Monday rate of return). We calculate rates of returns of companies and the index by:

rates <- get_rates_from_prices(prices,
                               quote = "Close",
                               multi_day = TRUE,
                               compounding = "continuous")

rates_indx <- get_rates_from_prices(prices_indx, 
                                    quote = "Close",
                                    multi_day = TRUE,
                                    compounding = "continuous")

Applying a market model

To calculate expected rates of returns during the event period we need to train a market model based on date from the estimation period. apply_market_model goes over each company (column) in rates and calibrates a chosen model. The function returns a list of S3 returns class objects, which can be passed to parametric_tests and nonparametric_tests. For our case we use the single-index market model:

securities_returns <- apply_market_model(
  rates = rates,
  regressor = rates_indx,
  same_regressor_for_all = TRUE,
  market_model = "sim",
  estimation_method = "ols",
  estimation_start = as.Date("2019-04-01"),
  estimation_end = as.Date("2020-03-13")
)

Tests

The event study utilizes both parametric and nonparametric tests. Parametric tests are Brown and Warner (1980), Brown and Warner (1985), t-test, Patell (1976), Boehmer (1991), and Lamb (1995) tests. To avoid calling all these tests separately, one can call parametric_tests with all = TRUE. The same applies to nonparametric tests, i.e. simple binomial sign, binomial sign, Corrado (1992), rank, modified rank, and Wilcoxon signed rank tests can be called all at once by specifying all = TRUE in nonparametric_tests. These tests are also available separately. Both functions return data frames with statistics and significance for each date in the event period. See ?parametric_tests and ?nonparametric_tests for references and more details.

parametric_tests(list_of_returns = securities_returns,
                 event_start = as.Date("2020-03-16"),
                 event_end = as.Date("2020-03-20"))
#>         date   weekday percentage        mean bw_1980_stat bw_1980_signif
#> 1 2020-03-16    Monday        100  0.02329740     2.486449             **
#> 2 2020-03-17   Tuesday        100 -0.03157867    -3.370281            ***
#> 3 2020-03-18 Wednesday        100 -0.01617850    -1.726675              *
#> 4 2020-03-19  Thursday        100  0.07672027     8.188085            ***
#> 5 2020-03-20    Friday        100  0.05840545     6.233409            ***
#>   bw_1985_stat bw_1985_signif t_test_stat t_test_signif    pt_stat pt_signif
#> 1     1.858514              *   1.3941497                2.5507399        **
#> 2    -2.519140             **  -1.2769075               -2.9495613       ***
#> 3    -1.290615                 -0.4023135                0.3283654          
#> 4     6.120241            ***   1.7958275                8.4216222       ***
#> 5     4.659205            ***   5.1233281           ***  6.3196352       ***
#>       bh_stat bh_signif  lmb_stat lmb_signif
#> 1  1.20471169            1.620049           
#> 2 -1.17531339           -2.435456         **
#> 3  0.07606284           -1.254626           
#> 4  2.16657728         *  6.105794        ***
#> 5  8.65212561       ***  4.565195        ***

nonparametric_tests(list_of_returns = securities_returns,
                    event_start = as.Date("2020-03-16"),
                    event_end = as.Date("2020-03-20"))
#>         date   weekday percentage sign_stat sign_signif gsign_stat gsign_signif
#> 1 2020-03-16    Monday        100 1.1338934              1.1516761             
#> 2 2020-03-17   Tuesday        100 0.3779645              0.3957301             
#> 3 2020-03-18 Wednesday        100 1.1338934              1.1516761             
#> 4 2020-03-19  Thursday        100 1.8898224           *  1.9076220            *
#> 5 2020-03-20    Friday        100 2.6457513         ***  2.6635680          ***
#>   csign_stat csign_signif  rank_stat rank_signif mrank_stat mrank_signif
#> 1  0.8636930               0.7962064              0.8499246             
#> 2  0.2878977              -0.1857202             -0.2283881             
#> 3  0.8636930               1.0205417              0.9865178             
#> 4  1.4394883               2.1127972          **  2.1665371           **
#> 5  2.0152836           **  2.9255529         ***  2.9786668          ***
#>   wlcx_stat wlcx_signif
#> 1        23           *
#> 2        10            
#> 3        15            
#> 4        27         ***
#> 5        28         ***

Using magrittr

One can notice that the result of the previous function is passed to the next function. In order to avoid naming all the intermediate objects or using a function composition, it possible to utilize a power of the pipe operator from magrittr. The above code can be rewritten with:

library(magrittr)

rates_indx <- get_prices_from_tickers("^GSPC",
                                      start = as.Date("2019-04-01"),
                                      end = as.Date("2020-04-01"),
                                      quote = "Close",
                                      retclass = "zoo") %>%
  get_rates_from_prices(quote = "Close",
                        multi_day = TRUE,
                        compounding = "continuous")

tickers <- c("AMZN", "ZM", "UBER", "NFLX", "SHOP", "FB", "UPWK")

param_tests <- get_prices_from_tickers(tickers,
                                             start = as.Date("2019-04-01"),
                                             end = as.Date("2020-04-01"),
                                             quote = "Close",
                                             retclass = "zoo") %>%
  get_rates_from_prices(quote = "Close",
                        multi_day = TRUE,
                        compounding = "continuous") %>%
  apply_market_model(regressor = rates_indx,
                     same_regressor_for_all = TRUE,
                     market_model = "sim",
                     estimation_method = "ols",
                     estimation_start = as.Date("2019-04-01"),
                     estimation_end = as.Date("2020-03-13")) %>%
  parametric_tests(event_start = as.Date("2020-03-16"),
                   event_end = as.Date("2020-03-20"))