Intro to Investment Suite

Alejandro

2019-12-20

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%