Search code examples
rquantmod

Matrix imported by getSplits (quantmod) is shaped in different way based on symbols


I'm trying to import stock data (split), and I have a problem. There is no issue if all symbols contain split. When one of them does not have split, however, the matrix are shaped in different way and wrong.

Here are codes that I use:

library(quantmod)

Tick <- c("AA","ARGO")

split_test <- Reduce(merge, lapply(Tick, function(x) {
  tryCatch({
    getSplits(x, from= "2016-01-04", to="2017-03-09", src="yahoo")
  }, error = function(e) {}
  )
}))

and the outcome is fine:

               AA.spl         ARGO.spl
2016-05-27     N/A            0.9091
2016-10-06     3.000            N/A
2016-11-01     0.801            N/A 

The problem is that if Tick includes "A" which does not have split , the outcome is quite different and wrong:

Tick <- c("A","AA","ARGO")

     x     AA.spl         ARGO.spl
1    N/A    3.000         0.9091
2    N/A    0.801         0.9091

Dates are not shown up anymore, and the value is also wrong(same number repeats). How do I get outcome like first one even though a stock has no split? If it is possible I want "A" is just shown up as N/As like:

             A.spl       AA.spl         ARGO.spl
2016-05-27   N/A          N/A            0.9091
2016-10-06   N/A         3.000            N/A
2016-11-01   N/A         0.801            N/A 

getDividends

Tick <- c("A","AA","AADR","AAN","AAP","AAT")

Test <-
  lapply(Tick,
         function(x) {
           try(getDividends(x, from= "2016-01-04", to="2016-03-15", src="yahoo"),
               silent = TRUE)
         })
names(Test) <- Tick

# convert NA to xts object with date < 1900
Test <-
  lapply(Test,
         function(x) {
           if (identical(x, NA)) xts(NA, .Date(-4e4)) else x
         })

# merge and remove pre-1900 date
Test <- Reduce(merge, Test)["1900/"]

Solution

  • getSplits() just returns NA for "A", not an xts object. And "A" is first in your Tick vector, so the merge() method for xts objects isn't being called (because of how S3 method dispatch works).

    You could use cbind() instead of merge() because method dispatch for cbind() works different. But I wouldn't suggest that solution, because you're not likely to remember that subtle difference between the two functions.

    Instead, I would pre-process your list of splits before merging.

    split_test <-
      lapply(Tick,
             function(x) {
               try(getSplits(x, from= "2016-01-04", to="2017-03-09", src="yahoo"),
                   silent = TRUE)
             })
    names(split_test) <- Tick
    
    # convert NA to xts object with date < 1900
    split_test <-
      lapply(split_test,
             function(x) {
               if (identical(x, NA)) xts(NA, .Date(-4e4)) else x
             })
    
    # merge and remove pre-1900 date
    split_test <- Reduce(merge, split_test)["1900/"]
    # set names
    names(split_test) <- Tick
    
    split_test
    ##               AA  A      ARGO
    ## 2016-05-27    NA NA 0.9090909
    ## 2016-10-06 3.000 NA        NA
    ## 2016-11-01 0.801 NA        NA
    
    

    The code checks for list elements that are only NA, and replaces the NA with an xts object that has a date index before 1900. That date will be easy to identify in the output.

    Then we merge all the list elements together using Reduce(), like you did. And we set the names to your Tick vector, so the column that didn't have any data has the correct ticker name.


    Update for getDividends(), since it returns a zero-width xts object when there are no dividends, not NA like getSplits() does. We can add another check to the function that adjusts the xts objects with NA.

    Let's move this to a function, since you need it in more than one place. We can add another condition to the if statement.

    fill_missing <-
    function(x)
    {
        y <- x
        if (identical(x, NA) || length(x) < 1) {
            y <- xts(NA, .Date(-4e4))
        }
        y
    }
    

    Now use this function in your lapply() call and do the same Reduce() call. Then you can set names again, if you need to.

    Test <- lapply(Test, fill_missing)
    out <- Reduce(merge, Test)["1900/"]
    out <- out[,Tick]