Search code examples
rquantmodmoving-average

data length cannot be over width of moving average


I use quantmod, to calculate the moving average over 2000 dataframes with loop

price = xts object

price <- cbind(price, SMA(price, 5), SMA(price, 10),
SMA(price, 20), SMA(price, 60), SMA(price, 120),
SMA(price, 180), SMA(price, 240))

But some data don't exceed the number of width, stop running in the middle. In that case, I just want to fill NA only. I need some support to solve this problem. Or if I need to use any other package for solving this problem, let me know

Thanks


Solution

  • Moving average functions give an error when the chosen period is longer than the available data. As @RuiBarradas mentions in the comment, for a SMA zoo::rollmean could work. As you need to loop over quite a few data.frames a function is easier. The function below could be used in an lapply function or just in a loop.

    I created a sub function inside the bigger function to check if the chosen period is bigger than the rows supplied. If so, return a vector of NA's else return a SMA. After that, loop over the periods to return a data.frame with the supplied price column and all the SMA columns with a name so you can see which SMA is in which column.

    Note that there is no error handling in case of incorrect inputs. Sample data below.

    # periods for the SMA
    periods <- c(5, 10, 20, 60, 120, 180, 240)
    
    get_smas <- function(price, n) {
     
       my_sma <- function(x, n = 10) {
        
        if (n < 1 || n > NROW(x)) {
          out <- rep(NA_real_, NROW(x))
        } else {
          # change SMA for EMA if you want the EMA's
          out <- TTR::SMA(x, n = n)
        }
        out
      }
      
      # combine the price column with the ma's. Reduce works backwards, so price column last
      price_combined <- Reduce(cbind, lapply(n, function(x) my_sma(price, n = x)), price)
      # turn matrix into data.frame
      price_combined <- data.frame(price_combined)
      # rename columns, assuming price column has a column name.
      # change paste0 value from SMA to EMA if EMA is used.
      names(price_combined) <-  c(names(price_combined)[1], paste0("SMA_", n))
      price_combined
    }
    
    # supply a price and a vector of periods
    my_prices <- get_smas(price, periods)
    head(my_prices, 2)
       Close SMA_5 SMA_10 SMA_20 SMA_60 SMA_120 SMA_180 SMA_240
    1 182.01    NA     NA     NA     NA      NA      NA      NA
    2 179.70    NA     NA     NA     NA      NA      NA      NA
    
    tail(my_prices, 2)
         Close   SMA_5  SMA_10  SMA_20   SMA_60  SMA_120 SMA_180 SMA_240
    142 156.79 154.156 152.053 147.475 145.4393 156.1770      NA      NA
    143 157.35 154.556 152.941 148.381 145.4292 156.0474      NA      NA
    

    data:

    # close prices of aapl from 2022-01-03 to 2022-07-28
    price <- structure(list(Close = c(182.009995, 179.699997, 174.919998, 
    172, 172.169998, 172.190002, 175.080002, 175.529999, 172.190002, 
    173.070007, 169.800003, 166.229996, 164.509995, 162.410004, 161.619995, 
    159.779999, 159.690002, 159.220001, 170.330002, 174.779999, 174.610001, 
    175.839996, 172.899994, 172.389999, 171.660004, 174.830002, 176.279999, 
    172.119995, 168.639999, 168.880005, 172.789993, 172.550003, 168.880005, 
    167.300003, 164.320007, 160.070007, 162.740005, 164.850006, 165.119995, 
    163.199997, 166.559998, 166.229996, 163.169998, 159.300003, 157.440002, 
    162.949997, 158.520004, 154.729996, 150.619995, 155.089996, 159.589996, 
    160.619995, 163.979996, 165.380005, 168.820007, 170.210007, 174.070007, 
    174.720001, 175.600006, 178.960007, 177.770004, 174.610001, 174.309998, 
    178.440002, 175.059998, 171.830002, 172.139999, 170.089996, 165.75, 
    167.660004, 170.399994, 165.289993, 165.070007, 167.399994, 167.229996, 
    166.419998, 161.789993, 162.880005, 156.800003, 156.570007, 163.639999, 
    157.649994, 157.960007, 159.479996, 166.020004, 156.770004, 157.279999, 
    152.059998, 154.509995, 146.5, 142.559998, 147.110001, 145.539993, 
    149.240005, 140.820007, 137.350006, 137.589996, 143.110001, 140.360001, 
    140.520004, 143.779999, 149.639999, 148.839996, 148.710007, 151.210007, 
    145.380005, 146.139999, 148.710007, 147.960007, 142.639999, 137.130005, 
    131.880005, 132.759995, 135.429993, 130.059998, 131.559998, 135.869995, 
    135.350006, 138.270004, 141.660004, 141.660004, 137.440002, 139.229996, 
    136.720001, 138.929993, 141.559998, 142.919998, 146.350006, 147.039993, 
    144.869995, 145.860001, 145.490005, 148.470001, 150.169998, 147.070007, 
    151, 153.039993, 155.350006, 154.089996, 152.949997, 151.600006, 
    156.789993, 157.350006)), class = "data.frame", row.names = c(NA, 
    -143L))