# Load required packages
library(data.table)
# output line width (chars)
options(width = 200)Measuring Gap Opening Contributions in Stock Returns Using R
This post is the continuation of a previous post: Calculating the Weight of Gap Openings in a Stock Using SQL; the subject is the same but the application is via R programming language. The stock data includes dividend adjustments.
# read data file
MSFT <- fread("MSFT, 1D adjusted with dividents.csv", sep = ",")
# select necessary columns and rename column "time" as "date"
MSFT_open_close <- MSFT[, .(date = time, open, close)]
# filter year 2023
MSFT_open_close <- MSFT_open_close[date >= "2023-01-01" & date < "2024-01-01"]The structure of the table MSFT_open_close is as follows:
head(MSFT_open_close) date open close
<IDat> <num> <num>
1: 2023-01-03 239.6339 236.1835
2: 2023-01-04 228.9821 225.8521
3: 2023-01-05 223.9790 219.1584
4: 2023-01-06 219.8386 221.7412
5: 2023-01-09 223.2397 223.9002
6: 2023-01-10 224.5262 225.6056
Let’s do a warm-up round to calculate the columns for the daily changes, the lagged closings, and the opening gap.
MSFT_open_close[, daily_diff := close - open]
MSFT_open_close[, date := as.Date(date)]
MSFT_open_close[, close_lag_1 := shift(close, 1, type = "lag")]
MSFT_open_close[, opening_gap := open - shift(close, 1, type = "lag")]
head(MSFT_open_close) date open close daily_diff close_lag_1 opening_gap
<Date> <num> <num> <num> <num> <num>
1: 2023-01-03 239.6339 236.1835 -3.4503813 NA NA
2: 2023-01-04 228.9821 225.8521 -3.1299887 236.1835 -7.2014387
3: 2023-01-05 223.9790 219.1584 -4.8206756 225.8521 -1.8730641
4: 2023-01-06 219.8386 221.7412 1.9026388 219.1584 0.6802180
5: 2023-01-09 223.2397 223.9002 0.6605016 221.7412 1.4984513
6: 2023-01-10 224.5262 225.6056 1.0794764 223.9002 0.6259977
Now, it’s time to add the rest of the calculations one by one:
aggregations <- MSFT_open_close[, .(
beginning_of_period = min(date),
end_of_period = max(date),
sum_gap_openings = sum(opening_gap, na.rm = TRUE),
sum_intraday_changes = sum(daily_diff),
opening_period = MSFT_open_close[date == min(date), open],
closing_period = MSFT_open_close[date == max(date), close]
)]
aggregations beginning_of_period end_of_period sum_gap_openings sum_intraday_changes opening_period closing_period
<Date> <Date> <num> <num> <num> <num>
1: 2023-01-03 2023-12-29 72.6508 61.71129 239.6339 373.996
aggregations[, .(
opening_period,
closing_period,
pct_change_period = round(closing_period / opening_period - 1, 2),
sum_gap_openings,
sum_intraday_changes,
pct_gap_openings = round(sum_gap_openings / (closing_period - opening_period), 2),
pct_intraday_changes = round(sum_intraday_changes / (closing_period - opening_period), 2)
)] opening_period closing_period pct_change_period sum_gap_openings sum_intraday_changes pct_gap_openings pct_intraday_changes
<num> <num> <num> <num> <num> <num> <num>
1: 239.6339 373.996 0.56 72.6508 61.71129 0.54 0.46
Conclusion
In 2023, MSFT increased by 56%. Within the total increase in 2023, the weight of gap openings was 54%, and the weight of intraday changes was 46%.
As an extreme example, if one buys at the opening price at the beginning of each trading day and sells at the closing price, 54% of the total rise during the year would be missed compared to buying at the beginning of 2023 and selling at the end. In other words, 54% of the annual increase would be missed; this means missing out on a (56% x 54% = ) 30% profit relative to the index value at the beginning of the year. This shows that gap openings can have a significant impact in the long term.