joshuaulrich / quantmod

Quantitative Financial Modelling Framework
http://www.quantmod.com/
GNU General Public License v3.0
794 stars 219 forks source link

Possible unexpected behaviour when index is `POSIXct`-class #411

Open serkor1 opened 4 months ago

serkor1 commented 4 months ago

Description

If the Sys.timezone() is different from xts::tzone() and the index is POSIXct POSIXt class, then quantmod::specifyModel() fails, and this affects quantmod::buildModel() and quantmod::tradeModel(). I believe this is unexpected because in both cases xts::tzone() is UTC.

I have created some synthetic OHLC-data to reproduce the behavior, display the expected behavior and showing a manual fix.

Expected behavior

set.seed(1903)

# 0) create generic
# OHLC zoo object
open <- runif(n = 50,min = 10, max = 100)

ticker <- zoo::as.zoo(
  cbind(
    Open  = open,
    High  = open + 1,
    Low   = open - 1,
    Close = open + 0.5
  )
)

# 1) daily index
zoo::index(ticker) <- seq(
  from = Sys.Date(),
  by = "+1 day",
  length.out = nrow(ticker)
)

# 2) convert to xts
ticker <- xts::as.xts(
  ticker
)

# 3) Specify  and run model
# accordingly
model <- quantmod::buildModel(
  x = quantmod::specifyModel(
    quantmod::Next(quantmod::OpCl(ticker)) ~ quantmod::Lag(quantmod::OpHi(ticker))
  ),
  method = "lm",
  training.per = c(zoo::index(ticker)[1],zoo::index(ticker)[10])
)
#> Registered S3 method overwritten by 'quantmod':
#>   method            from
#>   as.zoo.data.frame zoo

# 3.1) Trade modell
quantmod::tradeModel(
  x = model
)
#> Warning in modelReturn(quantmodResults, trade.dates = trade.dates, leverage =
#> leverage, : Model results are all one direction.
#> 
#>   Model:  lm1708018884.17448 
#> 
#>   C.A.G.R.:  1272.66%    H.P.R.:  48.44% 
#> 
#>   Returns by period summary:
#> 
#>             weekly monthly quarterly yearly
#>     Max.     9.61%  37.81%    40.81% 46.68%
#>     3rd Qu.  7.65%  20.99%    31.65% 46.68%
#>     Mean     6.61%  14.72%    22.49% 46.68%
#>     Median   6.30%   4.17%    22.49% 46.68%
#>     2rd Qu.  5.45%   3.17%    13.33% 46.68%
#>     Min.     4.17%   2.18%     4.17% 46.68%
#> 
#>   Period to date returns:
#> 
#>              weekly monthly quarterly yearly
#>               4.17%   4.17%     4.17% 46.68%

Created on 2024-02-15 with reprex v2.1.0

Minimal, reproducible example

Where it fails

Here I change the index to POSIXct as this supports smaller granularity.

set.seed(1903)

# 0) create generic
# OHLC zoo object
open <- runif(n = 50,min = 10, max = 100)

ticker <- zoo::as.zoo(
  cbind(
    Open  = open,
    High  = open + 1,
    Low   = open - 1,
    Close = open + 0.5
  )
)

# 1) daily index
zoo::index(ticker) <- seq(
  from =  as.POSIXct(Sys.Date(), tz = "UTC", origin = '1970-01-01'),
  by = "+1 day",
  length.out = nrow(ticker)
)

# 2) convert to xts
ticker <- xts::as.xts(
  ticker
)

# 3) Specify  and run model
# accordingly
model <- quantmod::buildModel(
  x = quantmod::specifyModel(
    quantmod::Next(quantmod::OpCl(ticker)) ~ quantmod::Lag(quantmod::OpHi(ticker))
  ),
  method = "lm",
  training.per = c(zoo::index(ticker)[1],zoo::index(ticker)[10])
)

# 3.1) Trade modell
quantmod::tradeModel(
  x = model
)
#> Error in eval(expr, envir, enclos): object 'model' not found

Created on 2024-02-15 with reprex v2.1.0

Manual fix

I can fix this by either setting Sys.setenv(TZ = "UTC") or changing the index to a Date-class. (But the latter fix is not desirable for if one is to apply the quantmod-functions to, say, hourly data)

set.seed(1903)

# 0) create generic
# OHLC zoo object
open <- runif(n = 50,min = 10, max = 100)

ticker <- zoo::as.zoo(
  cbind(
    Open  = open,
    High  = open + 1,
    Low   = open - 1,
    Close = open + 0.5
  )
)

# 1) daily index
zoo::index(ticker) <- seq(
  from =  as.POSIXct(Sys.Date(), tz = "UTC", origin = '1970-01-01'),
  by = "+1 day",
  length.out = nrow(ticker)
)

# 2) convert to xts
ticker <- xts::as.xts(
  ticker
)

# 2.1) set TZ
Sys.setenv(TZ = "UTC")

# 3) Specify  and run model
# accordingly
model <- quantmod::buildModel(
  x = quantmod::specifyModel(
    quantmod::Next(quantmod::OpCl(ticker)) ~ quantmod::Lag(quantmod::OpHi(ticker))
  ),
  method = "lm",
  training.per = c(zoo::index(ticker)[1],zoo::index(ticker)[10])
)
#> Registered S3 method overwritten by 'quantmod':
#>   method            from
#>   as.zoo.data.frame zoo

# 3.1) Trade modell
quantmod::tradeModel(
  x = model
)
#> Warning in modelReturn(quantmodResults, trade.dates = trade.dates, leverage =
#> leverage, : Model results are all one direction.
#> 
#>   Model:  lm1708019051.06961 
#> 
#>   C.A.G.R.:  1272.66%    H.P.R.:  48.44% 
#> 
#>   Returns by period summary:
#> 
#>             weekly monthly quarterly yearly
#>     Max.     9.61%  37.81%    40.81% 46.68%
#>     3rd Qu.  7.65%  20.99%    31.65% 46.68%
#>     Mean     6.61%  14.72%    22.49% 46.68%
#>     Median   6.30%   4.17%    22.49% 46.68%
#>     2rd Qu.  5.45%   3.17%    13.33% 46.68%
#>     Min.     4.17%   2.18%     4.17% 46.68%
#> 
#>   Period to date returns:
#> 
#>              weekly monthly quarterly yearly
#>               4.17%   4.17%     4.17% 46.68%

Created on 2024-02-15 with reprex v2.1.0

Session Info

R version 4.3.2 (2023-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 11 x64 (build 22631)

Matrix products: default

locale:
[1] LC_COLLATE=English_Denmark.utf8  LC_CTYPE=English_Denmark.utf8   
[3] LC_MONETARY=English_Denmark.utf8 LC_NUMERIC=C                    
[5] LC_TIME=English_Denmark.utf8    

time zone: Europe/Copenhagen
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] usethis_2.2.2

loaded via a namespace (and not attached):
 [1] compiler_4.3.2    magrittr_2.0.3    cli_3.6.2         tools_4.3.2      
 [5] fs_1.6.3          glue_1.6.2        rstudioapi_0.15.0 vctrs_0.6.5      
 [9] lifecycle_1.0.4   rlang_1.1.3       purrr_1.0.2 

Disclaimer: I recently moved to Windows, and I don't remember experiencing this issue on Linux. So it might just be me who are missing something.