InvestmentSuite
is designed for quantitative analysis on time-series. The functions work on time-series in data.frame
structures organized by column with dates as Date
class in the first column. The ETF
data provides an example of a properly structured data.frame
for this package.
library(InvestmentSuite)
data(ETF)
head(ret)
#> date ivv iwm vtv iwf
#> 2595 2010-09-15 0.0037145131 0.005654716 0.002076950 0.004000800
#> 2596 2010-09-16 -0.0006167944 -0.006387943 -0.002257336 0.001793186
#> 2597 2010-09-17 0.0001763357 0.004157684 -0.000617030 0.002585521
#> 2598 2010-09-20 0.0160437236 0.027794817 0.015902449 0.015076374
#> 2599 2010-09-21 -0.0024292903 -0.005856242 -0.002890847 -0.001172562
#> 2600 2010-09-22 -0.0044355540 -0.011856521 -0.006704592 -0.003326159
#> efa vwo tlt ief emb
#> 2595 0.0003694809 0.0006841505 -0.014834794 -0.003478973 0.0038475632
#> 2596 -0.0048014774 -0.0045578851 -0.009875819 -0.003491118 -0.0002737726
#> 2597 -0.0048246428 -0.0004578755 0.004048983 0.002266873 -0.0004564126
#> 2598 0.0173410405 0.0160329821 0.005803088 0.003084199 -0.0001826484
#> 2599 -0.0001832845 -0.0006762849 0.013886173 0.009326637 0.0011874315
#> 2600 -0.0014665445 0.0002255809 0.008969907 0.001523152 0.0031019068
#> lqd hyg vnq amj igf
#> 2595 -0.0016140603 -0.0009012054 0.008680883 -0.0015156108 -0.002976190
#> 2596 -0.0031435243 0.0016912842 -0.005238541 -0.0015179114 -0.001194030
#> 2597 0.0032435355 0.0018009905 0.003009216 0.0009121313 -0.006873879
#> 2598 0.0012572968 0.0015730337 0.024376524 0.0069866343 0.012338249
#> 2599 0.0076240022 -0.0016827462 -0.014643969 0.0009049774 0.002675386
#> 2600 -0.0006231084 -0.0040453984 -0.013561211 0.0036166365 -0.001185888
#> gnr gld
#> 2595 -0.0067527309 -0.0006772883
#> 2596 0.0029994001 0.0055672099
#> 2597 -0.0019956140 -0.0007020782
#> 2598 0.0113883568 0.0026296244
#> 2599 -0.0003950227 0.0091294947
#> 2600 -0.0047421458 0.0015078168
For users more acustom to using xts
structures there’s an xts_to_dataframe
function to convert xts
structures to the data.frame
time-series structure this package functions work on.
x <- matrix(runif(25), ncol = 5)
sample_xts <- xts::xts(x, seq.Date(from = as.Date('2019-01-01'),
length.out = 5,
by = 'day'))
sample_xts
#> [,1] [,2] [,3] [,4] [,5]
#> 2019-01-01 0.65777198 0.8416523 0.57633464 0.3237590 0.8977583
#> 2019-01-02 0.84699544 0.3877873 0.18734521 0.5818808 0.3234651
#> 2019-01-03 0.67986409 0.3195843 0.33839227 0.6258543 0.6274085
#> 2019-01-04 0.23667400 0.9561182 0.18086907 0.4152938 0.6923032
#> 2019-01-05 0.02841845 0.8006635 0.09991763 0.9122559 0.7170409
xts_to_dataframe(sample_xts)
#> date X1 X2 X3 X4 X5
#> 1 2019-01-01 0.65777198 0.8416523 0.57633464 0.3237590 0.8977583
#> 2 2019-01-02 0.84699544 0.3877873 0.18734521 0.5818808 0.3234651
#> 3 2019-01-03 0.67986409 0.3195843 0.33839227 0.6258543 0.6274085
#> 4 2019-01-04 0.23667400 0.9561182 0.18086907 0.4152938 0.6923032
#> 5 2019-01-05 0.02841845 0.8006635 0.09991763 0.9122559 0.7170409
InvestmentSuite
has time-series utilities to change the return or price frequency and convert between prices and returns.
ret_monthly <- change_freq(ret, 'm')
head(ret_monthly)
#> date ivv iwm vtv iwf
#> 1 2010-10-31 0.034653465 0.04743609 0.027015982 0.044371246
#> 11 2010-11-30 0.033744649 0.04077314 0.030083400 0.034879406
#> 30 2010-12-31 0.031570849 0.07520170 0.036024968 0.033272663
#> 21 2011-01-31 0.032431577 0.02077001 0.035961791 0.033606129
#> 40 2011-02-28 0.034630708 0.03411911 0.039595010 0.036219677
#> 49 2011-03-31 -0.008293839 0.01193097 -0.006073359 -0.009754511
#> efa vwo tlt ief emb
#> 1 0.041501263 0.045314562 -0.0006056599 0.0095230498 0.024668449
#> 11 0.030145530 0.037878788 -0.0403219667 -0.0002187066 0.008572396
#> 30 -0.011711915 -0.006894061 -0.0436624626 -0.0308524002 -0.037797149
#> 21 0.035211268 0.015245296 -0.0247627549 -0.0215247648 -0.006338826
#> 40 0.027418284 -0.028436989 -0.0084810757 -0.0055637613 -0.014741369
#> 49 -0.002139771 0.030743314 0.0198257465 0.0173436116 0.008920549
#> lqd hyg vnq amj igf
#> 1 0.005288547 0.018474929 0.02795711 0.05171903 0.045839416
#> 11 0.002530059 0.019330111 0.02910005 0.04128505 0.025974316
#> 30 -0.026639931 -0.004578866 -0.02111725 -0.00383969 -0.025044843
#> 21 0.002062867 0.017166907 0.03406633 0.02861073 0.043354250
#> 40 0.001848037 0.013674391 0.04340248 0.03549177 0.024603609
#> 49 0.005720222 0.002933073 -0.01616186 -0.00418082 0.000266809
#> gnr gld
#> 1 0.065298143 0.053145760
#> 11 0.047164617 0.022486085
#> 30 0.040354929 0.009653070
#> 21 0.027521526 -0.007979297
#> 40 0.029245762 -0.002456522
#> 49 0.008301405 0.019460849
price_index <- ret_to_price(ret_monthly, init_val = 1)
head(price_index)
#> date ivv iwm vtv iwf efa vwo
#> 1 2010-10-30 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000
#> 113 2010-10-31 1.034653 1.047436 1.027016 1.044371 1.041501 1.045315
#> 11 2010-11-30 1.069567 1.090143 1.057912 1.080798 1.072898 1.084910
#> 30 2010-12-31 1.103335 1.172124 1.096023 1.116759 1.060332 1.077430
#> 21 2011-01-31 1.139118 1.196469 1.135438 1.154289 1.097668 1.093856
#> 40 2011-02-28 1.178566 1.237291 1.180396 1.196097 1.127764 1.062750
#> tlt ief emb lqd hyg vnq amj
#> 1 1.0000000 1.0000000 1.0000000 1.0000000 1.000000 1.000000 1.000000
#> 113 0.9993943 1.0095230 1.0246684 1.0052885 1.018475 1.027957 1.051719
#> 11 0.9590968 1.0093023 1.0334523 1.0078320 1.038162 1.057871 1.095139
#> 30 0.9172203 0.9781629 0.9943908 0.9809834 1.033409 1.035531 1.090934
#> 21 0.8945074 0.9571081 0.9880875 0.9830071 1.051149 1.070808 1.122147
#> 40 0.8869210 0.9517830 0.9735217 0.9848237 1.065523 1.117284 1.161974
#> igf gnr gld
#> 1 1.000000 1.000000 1.000000
#> 113 1.045839 1.065298 1.053146
#> 11 1.073004 1.115543 1.076827
#> 30 1.046131 1.160560 1.087222
#> 21 1.091485 1.192501 1.078546
#> 40 1.118340 1.227376 1.075897
The data.frame
time-series are organized by columns for ease of time-series analysis calculations, however they can easily be converted into a ‘tidy’ structure with tidy_ret
to work with tidyverse packages such as ggplot2
.
library(ggplot2)
plot_data <- tidy_ret(price_index[, 1:4])
head(plot_data)
#> date series values
#> 1 2010-10-30 ivv 1.000000
#> 2 2010-10-30 iwm 1.000000
#> 3 2010-10-30 vtv 1.000000
#> 4 2010-10-31 ivv 1.034653
#> 5 2010-10-31 iwm 1.047436
#> 6 2010-10-31 vtv 1.027016
ggplot(plot_data, aes(x = date, y = values, color = series)) +
geom_line()
The rebal
function combines the return data.frame
into a portfolio time-series based on the wgt
parameter. The wgt
parameter can be a numeric vector or a data.frame
of a time-series of weights organized in the same manner as the ret
time-series. If the wgt
parameter is specified as a vector then the reb_freq
parameter can be used to create periodic rebalance dates to bring the weights back to the target wgt
. The code below will show how this works with a quarterly rebalance assumption. Note, if passing a numeric vector of wgt
, leaving the reb_freq
as NA
will run the rebal
as a buy-and-hold strategy without periodic rebalances.
n_assets <- ncol(ret) - 1
equal_wgt <- rep(1 / n_assets, n_assets)
rebal_wgt <- auto_reb_wgt(equal_wgt, 'q')
#> [1] "date_start passed as NA, setting to 1970-01-01"
head(rebal_wgt)
#> date X1 X2 X3 X4 X5 X6 X7 X8
#> 1 1970-03-31 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 51 1970-06-30 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 101 1970-09-30 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 151 1970-12-31 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 2 1971-03-31 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 52 1971-06-30 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> X9 X10 X11 X12 X13 X14 X15 X16
#> 1 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 51 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 101 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 151 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 2 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
#> 52 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625 0.0625
port <- rebal(ret = ret, ret_freq = 'd', wgt = equal_wgt, reb_freq = 'q')
#> [1] "date_start passed as NA, setting to 1970-01-01"
head(port$port_index)
#> date port_index
#> 1 2010-09-14 100.00000
#> 2 2010-09-15 99.97674
#> 3 2010-09-16 99.78113
#> 4 2010-09-17 99.82025
#> 5 2010-09-20 100.92730
#> 6 2010-09-21 101.02068
ggplot(port$port_index, aes(x = date, y = port_index)) +
geom_line()
The result of the rebal
function is a list containing the asset returns and historical weights as well as the portfolio returns. The portfolio list contains all the data needed for calculating a contribution to return.
# 2019 year-to-date contribution to return
contr_to_ret(port, date_start = as.Date('2019-01-01'))
#> asset contr.to.ret
#> 1 ivv 0.0145467986
#> 2 iwm 0.0150721350
#> 3 vtv 0.0140905062
#> 4 iwf 0.0154287364
#> 5 efa 0.0148647174
#> 6 vwo 0.0162428097
#> 7 tlt 0.0080406452
#> 8 ief 0.0090721972
#> 9 emb 0.0106846475
#> 10 lqd 0.0109380005
#> 11 hyg 0.0110506921
#> 12 vnq 0.0074849883
#> 13 amj 0.0031390640
#> 14 igf 0.0120231429
#> 15 gnr 0.0140749008
#> 16 gld 0.0086465533
#> 17 residual -0.0006972181
#> 18 total 0.1847033172
The run_reg
function runs a multi-variate regression on multiple y variables. The FF
data contains factor returns from Ken French’s data library. The result contains a list of fit from the lm()
results and tables to show the regression summary.
data(FF)
y <- ret[, 1:7]
x <- ff$ff_5
res <- run_reg(y = y, x = x, rf = ff$ff_rf, freq = 'w')
res$fmt
#> asset Mkt.RF SMB HML RMW CMA Ann.Resid adj.r2
#> ivv ivv 0.96 -0.10 -0.06 -0.01 0.12 0.81% 98.87%
#> iwm iwm 1.06 0.60 -0.02 -0.05 -0.11 (1.03%) 94.20%
#> vtv vtv 0.93 -0.11 0.10 -0.04 0.28 0.63% 93.83%
#> iwf iwf 0.97 -0.06 -0.29 -0.00 -0.00 1.47% 94.81%
#> efa efa 0.99 0.08 0.42 0.12 -0.37 (5.66%) 75.39%
#> vwo vwo 1.10 0.04 0.46 0.50 -0.57 (10.22%) 58.76%