I. Ozkan
Spring 2025
In this example, quantmod
and lubridate
packages are used. quantmod
package
is a non-complete package used for Quantitative Financial Modelling
Framework. It depends xts
and zoo
packages
hence we do not need to load these packages explicitely. Here the aim is
to give an example for lubridate
package usage.
It is not easy to manipulate time series data since all observations
have taken through time. In order to perform some operations one may
need to index time values properly. lubridate
package is
quite useful to deal with this type of data. As an example, we will use
real world data to show some functions fo this package.
First let’s downlad IBM stock price series.
quantmod
package provides getSymbols
function
that downloads historical data from some sources. See help page
(?getSymbols
).
# Load libraries
library(lubridate)
library(quantmod)
# Get Stock Price Series from yahoo finance (finance.yahoo.com)
getSymbols("IBM", from="2010-01-01")
## [1] "IBM"
## IBM.Open IBM.High IBM.Low IBM.Close IBM.Volume IBM.Adjusted
## 2010-01-04 125.4111 127.1224 125.0956 126.6252 6438444 74.26977
## 2010-01-05 125.8891 126.0516 124.3786 125.0956 7156104 73.37260
## 2010-01-06 124.9331 125.7075 124.1013 124.2830 5863144 72.89598
## 2010-01-07 124.1587 124.5220 123.2409 123.8528 6109268 72.64365
## 2010-01-08 123.3939 125.1625 123.3748 125.0956 4390271 73.37260
## 2010-01-11 125.2964 125.2964 123.0115 123.7859 5993998 72.60437
## IBM.Open IBM.High IBM.Low IBM.Close IBM.Volume IBM.Adjusted
## 2025-02-19 262.00 264.36 260.09 264.32 3718700 264.32
## 2025-02-20 263.65 265.09 262.15 264.74 4884800 264.74
## 2025-02-21 263.85 264.83 261.10 261.48 5667900 261.48
## 2025-02-24 261.50 263.85 259.58 261.87 4398100 261.87
## 2025-02-25 261.08 263.48 256.77 257.75 6292200 257.75
## 2025-02-26 258.10 258.33 254.41 255.84 3458800 255.84
Financial Data generally represented in a special format; Open-High-Low-Close-Volume (OHLC) format. Yahoo provides Adjusted price column. Please do refer to yahoo-finance web site.
Let’s use wday()
:day of Week, mday()
day of
month, and month()
functions of lubridate
package.
## [1] 2 3 4 5 6 2
## [1] Mon Tue Wed Thu Fri Mon
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
## Ord.factor w/ 7 levels "Sun"<"Mon"<"Tue"<..: 2 3 4 5 6 2
## [1] TRUE
## [1] "Mon" "Tue" "Wed" "Thu" "Fri" "Mon"
## [1] 58
## numeric(0)
## [1] 58
## [1] 4 5 6 7 8 11
## [1] 1
## [1] 4 5 6 7 8 11
## [1] 1 1 1 1 1 1
# Convert to monthly data using to.monthly() function. Then get the first few months
head(month(index(to.monthly(IBM)), label=T))
## [1] Jan Feb Mar Apr May Jun
## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun"
# last weekdays of months and of course weekdaysdate
# See http://stackoverflow.com/questions/33088424/r-find-last-weekday-of-month
last_weekday_o_month <- function(dx) {
ave(
paste(wday(dx, label=T, abbr=F)),
paste(month(dx, label=T, abbr = F)),
FUN = function(x) tail(x[ !(x %in% c("Saturday","Sunday")) ], 1)
)
}
last_weekdaydate_o_month <- function(dx){
ave(
dx,
# paste(month(dx, label=T, abbr = F)),
month(dx, label=T),
year(dx),
FUN = function(x) tail(x[ !(wday(x, label=T, abbr=F) %in% c("Saturday","Sunday")) ], 1)
)
}
# Get the last 25 end of months weekdays Adjusted close value..
Ad(IBM[tail(unique(last_weekdaydate_o_month(index(IBM))), 15),])
## IBM.Adjusted
## 2023-12-29 156.7993
## 2024-01-31 176.0793
## 2024-02-29 179.0100
## 2024-03-28 184.7471
## 2024-04-30 160.7926
## 2024-05-31 163.0239
## 2024-06-28 168.9840
## 2024-07-31 187.7339
## 2024-08-30 199.2222
## 2024-09-30 217.8996
## 2024-10-31 203.7462
## 2024-11-29 225.9050
## 2024-12-31 218.3752
## 2025-01-31 254.0078
## 2025-02-26 255.8400
Ok. Now lets obtain Adjusted price series and check the prices for
some specific day of the weeks. (Here Ad()
functin is used.
But you may get the very same data with referencing this column by its
number or name; IBM[,6]
, or
IBM[,"IBM.Adjusted"]
)
## IBM.Adjusted
## 2010-01-04 74.26977
## 2010-01-11 72.60437
## 2010-01-25 70.72028
## 2010-02-01 69.90721
## 2010-02-08 68.64843
## 2010-02-22 71.44778
## [1] Mon Mon Mon Mon Mon Mon
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
## [1] Fri Fri Fri Fri Fri Fri
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
# Get return series (cont. compounding, log(xt/xt-1)=log(xt)-log(xt-1))
ibm.ret <- diff(log(ibm))
# Mondays returns..
head(ibm.ret[wday(index(ibm.ret))==2])
## IBM.Adjusted
## 2010-01-04 NA
## 2010-01-11 -0.010525410
## 2010-01-25 0.004928166
## 2010-02-01 0.018457325
## 2010-02-08 -0.008903237
## 2010-02-22 -0.002676876
## IBM.Adjusted IBM.Adjusted.1
## 2010-01-04 74.26977 NA
## 2010-01-05 73.37260 -0.012153450
## 2010-01-06 72.89598 -0.006517020
## 2010-01-07 72.64365 -0.003467471
## 2010-01-08 73.37260 0.009984490
## 2010-01-11 72.60437 -0.010525410
## IBM.Adjusted IBM.Adjusted.1
## 2010-01-04 74.26977 NA
## 2010-01-11 72.60437 -0.010525410
## 2010-01-25 70.72028 0.004928166
## 2010-02-01 69.90721 0.018457325
## 2010-02-08 68.64843 -0.008903237
## 2010-02-22 71.44778 -0.002676876
Histogram for IBM stock price return series. This example includes comparing different day-of-week returns.
# Histogram: IBM returns.
hist(ibm.ret, breaks=100, main="Ibm's Cont. Comp. Returns", xlab="Returns")
# Histogram for mondays returns.
hist(ibm.ret[wday(index(ibm.ret))==2], breaks=50, main="Ibm's Monday Cont. Comp. Returns", xlab="Returns")
# Histogram for fridays returns. (slightly better shape?)
hist(ibm.ret[wday(index(ibm.ret))==6], breaks=50, main="Ibm's Friday Cont. Comp. Returns", xlab="Returns")
# Empirical Cumulative Distribution of Fridays Return
plot(ecdf(as.vector(ibm.ret[wday(index(ibm.ret))==6])), main="Friday ECDF")
# Comparison pf Empirical Cumulative Distributions of Monday (red) and Fridays (black) Return
plot(ecdf(as.vector(ibm.ret[wday(index(ibm.ret))==6])), main="ECDF Distributions")
lines(ecdf(as.vector(ibm.ret[wday(index(ibm.ret))==2])), col="red")
# What about mean returns of weekdays..
mean.ret <- cbind(
mean(ibm.ret[wday(index(ibm.ret))==2], na.rm=T),
mean(ibm.ret[wday(index(ibm.ret))==3], na.rm=T),
mean(ibm.ret[wday(index(ibm.ret))==4], na.rm=T),
mean(ibm.ret[wday(index(ibm.ret))==5], na.rm=T),
mean(ibm.ret[wday(index(ibm.ret))==6], na.rm=T))
colnames(mean.ret) <- levels(wday(index(ibm.ret), label=T))[2:6]
rownames(mean.ret) <- "Mean Returns"
# Lets see up to 6 digits..
round(mean.ret, 6)
## Mon Tue Wed Thu Fri
## Mean Returns 0.000935 8.4e-05 0.000331 0.000448 -0.000125
Let’s plot only Mondays Adjusted closes.
# zoo type..
plot(as.zoo(ibm[wday(index(ibm))==2]), main="IBM Mondays Prices", ylab="$", xlab="Date")
Now let’s play with monthly series..
# Convert to monthly series
ibm.m <- Ad(to.monthly(IBM))
# Get the return series similar to above
ibm.mret <- diff(log(ibm.m))
# The mean of all montly returns.. (up to 6 digits)
round(mean(ibm.mret, na.rm=T), 6)
## [1] 0.00727
# What about the yearly mean returns calculated from monthly returns.. (up to 6 digits).. Seems better than T-Bonds.. Remember utility functions..
round((1+mean(ibm.mret, na.rm=T))^12-1, 6)
## [1] 0.090812
# Here is the mean yearly return calculated from data
round(mean(diff(log(Ad(to.yearly(IBM)))), na.rm=T), 6)
## [1] 0.074354
# Get some mean return value fo specific months..
head(ibm.mret[month(index(ibm.mret))==1], na.rm=T)
## IBM.Adjusted
## Jan 2010 NA
## Jan 2011 0.09879775
## Jan 2012 0.04633200
## Jan 2013 0.05840194
## Jan 2014 -0.05981150
## Jan 2015 -0.04545790
# Mean January Returns.. (check your R and OS language for different month labels..)
mean(ibm.mret[month(index(ibm.mret))==1], na.rm=T)
## [1] 0.03461933
## [1] 0.007347965
One can compare returns distributions by using
Kolmogorov-Smirnov Tests. See ?ks.test
# An example.. Compare Mondays and Tuesdays..
ks.test(ibm.ret[wday(index(ibm.ret))==2], ibm.ret[wday(index(ibm.ret))==3])
##
## Asymptotic two-sample Kolmogorov-Smirnov test
##
## data: ibm.ret[wday(index(ibm.ret)) == 2] and ibm.ret[wday(index(ibm.ret)) == 3]
## D = 0.062899, p-value = 0.1049
## alternative hypothesis: two-sided
# Compare Mondays and Fridays..
ks.test(ibm.ret[wday(index(ibm.ret))==2], ibm.ret[wday(index(ibm.ret))==6])
##
## Asymptotic two-sample Kolmogorov-Smirnov test
##
## data: ibm.ret[wday(index(ibm.ret)) == 2] and ibm.ret[wday(index(ibm.ret)) == 6]
## D = 0.058942, p-value = 0.1545
## alternative hypothesis: two-sided
# Let's obtain all.. Create 5*5 matrix.. We use only upper-triangle
# See the structure of ks.test result first..
p.matr <- matrix(rep(0, 5*5), nrow=5)
colnames(p.matr) <- rownames(p.matr) <- levels(wday(index(ibm.ret), label=T))[2:6]
for (i in 2:6){
for (j in i:6){
p.matr[i-1,j-1] <- ks.test(as.numeric(ibm.ret[wday(index(ibm.ret))==i]), as.numeric(ibm.ret[wday(index(ibm.ret))==j]))$p.value
}
}
# p-vales matrix of Kolmogorov-Smirnov Tests
round(p.matr, 3)
## Mon Tue Wed Thu Fri
## Mon 1 0.177 0.651 0.303 0.425
## Tue 0 1.000 0.675 0.082 0.554
## Wed 0 0.000 1.000 0.689 0.971
## Thu 0 0.000 0.000 1.000 0.662
## Fri 0 0.000 0.000 0.000 1.000
# Let's obtain all months returns.. Create 12*12 matrix.. We use only upper-triangle
# See the structure of ks.test result first..
pm.matr <- matrix(rep(0, 12*12), nrow=12)
colnames(pm.matr) <- rownames(pm.matr) <- levels(month(index(ibm.mret), label=T))
for (i in 1:12){
for (j in i:12){
pm.matr[i,j] <- ks.test(as.numeric(ibm.mret[month(index(ibm.mret))==i]), as.numeric(ibm.mret[month(index(ibm.mret))==j]))$p.value
}
}
# p-vales matrix of Kolmogorov-Smirnov Tests
round(pm.matr, 3)
## Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## Jan 1 0.114 0.678 0.184 0.075 0.026 0.678 0.026 0.386 0.184 0.386 0.026
## Feb 0 1.000 0.817 0.729 0.630 0.630 0.520 0.331 0.552 0.043 0.134 0.630
## Mar 0 0.000 1.000 0.386 0.386 0.386 1.000 0.184 0.938 0.008 0.678 0.678
## Apr 0 0.000 0.000 1.000 0.938 0.678 0.386 0.938 0.678 0.386 0.184 0.678
## May 0 0.000 0.000 0.000 1.000 1.000 0.184 0.938 0.678 0.184 0.184 1.000
## Jun 0 0.000 0.000 0.000 0.000 1.000 0.184 0.938 0.938 0.075 0.184 1.000
## Jul 0 0.000 0.000 0.000 0.000 0.000 1.000 0.075 0.386 0.075 0.678 0.386
## Aug 0 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.678 0.386 0.184 0.938
## Sep 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.026 0.678 0.938
## Oct 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.002 0.075
## Nov 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.386
## Dec 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000
Repeat the above steps for BIST100
series that can be downloaded with:
getSymbols("XU100.IS", return.class="xts")