business-science / tidyquant

Bringing financial analysis to the tidyverse
https://business-science.github.io/tidyquant/
Other
855 stars 175 forks source link

tq_performance does not accept yearmon for date values, PerformanceAnalytics does #47

Closed apsteinmetz closed 7 years ago

apsteinmetz commented 7 years ago

When converting time series to monthly it makes sense to use the yearmon class. If you are comparing two assets from different data sources, the dates may not line up precisely. I found that happening using FRED with Yahoo. Similarly, trading days may not be equivalent across assets in different marketplaces, bonds and stocks, for example. Converting everything to yearmon when looking at monthly data forces alignment. In some cases it may introduce imprecision but for most uses it is fine. PerformanceAnalytics is fine with yearmon. tq_performance does not accept dates in yearmon form, which forces the user to convert to yearmon to ensure alignment, then convert back to date. It's not to tough to do, but it is an inconsistency.

set.seed(12345)
Ra_tibble<-tibble(date=as.yearmon("2009-01-01")+seq(1/12,3,1/12),
             Ra=rnorm(3*12,.06/12,.01))

Ra_xts<-as_xts(Ra_tibble,date_col = date)

#in tidyquant -------------------------
# yearmon for date NOT accepted
Ra_tibble %>%
  tq_performance(Ra = Ra, Rf = 0.02/12, performance_fun = SharpeRatio)

#Error in get_col_name_date_or_date_time(data) : 
#No date or POSIXct column found in `data`.

#fix it - easy enough!
Ra_tibble %>% 
  mutate(date=as.Date(date,frac=1)) %>%
  tq_performance(Ra = Ra, Rf = 0.02/12, performance_fun = SharpeRatio)
# A tibble: 1 × 3
#`ESSharpe(Rf=0.2%,p=95%)` `StdDevSharpe(Rf=0.2%,p=95%)` `VaRSharpe(Rf=0.2%,p=95%)`
#*                     <dbl>                         <dbl>                      <dbl>
#  1                 0.6415755                     0.6305925                  0.8175275

#in PerformanceAnalytics -------------------------
# yearmon for date accepted
SharpeRatio(Ra_xts$Ra,Rf=0.02/12)
#                                    Ra
#StdDev Sharpe (Rf=0.2%, p=95%): 0.6305925
#VaR Sharpe (Rf=0.2%, p=95%):    0.8175275
#ES Sharpe (Rf=0.2%, p=95%):     0.6415755
mdancho84 commented 7 years ago

Internally, the tq_performance function looks for a date column that is in either Date or POSIXct class, which is insufficient. We have a much better function in timekit, tk_get_timeseries_variables() that will find yearmon and yearqtr class rows and enable use in tq_performance() and all the other core tq_ functions.

mdancho84 commented 7 years ago

Also, we will be deprecating the tidyquant as_xts() and as_tibble() functions for the more robust functions from the timekit package: tk_xts() and tk_tbl(), respectively. You will not need to specify a "date" column as the timekit functions will detect any of the main time-based classes.

mdancho84 commented 7 years ago

I've got good news and bad news. The good news is that zoo yearmon and yearqtr are integrated now with via the merge to implement the timekit coercion functions, https://github.com/business-science/tidyquant/commit/352e30b870737b7389597b66d69cb93bbab857ad. However, there's a new issue #53 that's root is within dplyr (https://github.com/tidyverse/dplyr/issues/2921). The dplyr issue causes yearmon and yearqtr classes to convert to numeric during binding and grouping operations (which are essential to the tidyverse). Unfortunately, this will need to be handled within dplyr.

You can now do this:

library(tidyquant)
library(timekit)

set.seed(12345)
Ra <- rnorm(3*12,.06/12,.01)
Ra_tibble <- tibble(date = as.yearmon("2009-01-01")+seq(1/12,3,1/12),
                    Ra   = Ra)
Ra_xts <- tk_xts(Ra_tibble, silent = TRUE)

Ra_tibble %>%
    tq_performance(Ra = Ra, Rf = 0.02/12, performance_fun = SharpeRatio)
#> # A tibble: 1 x 3
#>   `ESSharpe(Rf=0.2%,p=95%)` `StdDevSharpe(Rf=0.2%,p=95%)`
#> *                     <dbl>                         <dbl>
#> 1                 0.4851708                     0.5995772
#> # ... with 1 more variables: `VaRSharpe(Rf=0.2%,p=95%)` <dbl>

Ra_xts$Ra %>%
    SharpeRatio(Rf = 0.02/12)
#>                                        Ra
#> StdDev Sharpe (Rf=0.2%, p=95%): 0.5995772
#> VaR Sharpe (Rf=0.2%, p=95%):    0.6748474
#> ES Sharpe (Rf=0.2%, p=95%):     0.4851708

You just can't do this:

library(tidyquant)
FANG %>%
    group_by(symbol) %>%
    tq_transmute(adjusted, periodReturn, period = "monthly") %>%
    mutate(date = as.yearmon(date))
#> Warning in mutate_impl(.data, dots): Vectorizing 'yearmon' elements may not
#> preserve their attributes

#> Warning in mutate_impl(.data, dots): Vectorizing 'yearmon' elements may not
#> preserve their attributes

#> Warning in mutate_impl(.data, dots): Vectorizing 'yearmon' elements may not
#> preserve their attributes

#> Warning in mutate_impl(.data, dots): Vectorizing 'yearmon' elements may not
#> preserve their attributes
#> # A tibble: 192 x 3
#> # Groups:   symbol [4]
#>    symbol     date monthly.returns
#>     <chr>    <dbl>           <dbl>
#>  1     FB 2013.000    0.1064285714
#>  2     FB 2013.083   -0.1204002582
#>  3     FB 2013.167   -0.0612844037
#>  4     FB 2013.250    0.0856137608
#>  5     FB 2013.333   -0.1231544833
#>  6     FB 2013.417    0.0217658727
#>  7     FB 2013.500    0.4790996977
#>  8     FB 2013.583    0.1220109272
#>  9     FB 2013.667    0.2165172871
#> 10     FB 2013.750   -0.0003981883
#> # ... with 182 more rows