I am trying to calculate a 20-day rolling percent change in R based off of a stock's closing price. Below is a sample of the most recent 100 days of closing price data. df$Close[1] is the most recent day, df$Close[2] is the previous day, and so on.
df$Close
[1] 342.94 346.22 346.18 335.24 330.45 334.20 325.45 333.79 334.90 341.66 333.74 334.49 329.75 329.82 330.56 322.81 317.87 306.84
[19] 310.39 310.60 324.46 338.03 333.12 341.06 337.25 341.01 345.30 338.69 340.77 342.96 347.56 340.89 327.74 327.64 335.37 338.62
[37] 341.13 335.85 331.62 328.08 329.98 323.57 316.92 312.22 315.81 328.69 324.61 341.88 340.78 339.99 335.34 324.76 328.53 324.54
[55] 323.77 325.45 330.05 329.22 333.64 332.96 326.23 343.01 339.39 339.61 340.65 353.58 352.96 345.96 343.21 357.48 355.70 364.72
[73] 373.06 373.92 376.53 376.51 378.69 378.00 377.57 382.18 376.26 375.28 382.05 379.38 380.66 372.63 364.38 368.39 365.51 363.35
[91] 359.37 355.12 355.45 358.45 366.56 363.18 362.65 359.96 361.13 361.61
Previously, I had used the following code to calculate the percent change:
PercChange(df, Var = 'Close', type = 'percent', NewVar = 'OneMonthChange', slideBy = 20)
which gave me the following output:
df$OneMonthChange
[1] 5.695617e-02 2.422862e-02 3.920509e-02 -1.706445e-02 -2.016308e-02 -1.997009e-02 -5.748624e-02 -1.446751e-02 -1.722569e-02
[10] -3.790530e-03 -3.976292e-02 -1.877438e-02 6.132910e-03 6.653644e-03 -1.434237e-02 -4.668950e-02 -6.818515e-02 -8.637785e-02
[19] -6.401906e-02 -5.327969e-02 -1.672829e-02 4.468894e-02 5.111700e-02 9.237076e-02 6.788892e-02 3.748213e-02 6.373802e-02
[28] -9.330759e-03 -2.934445e-05 8.735551e-03 3.644063e-02 4.966745e-02 -2.404651e-03 9.551981e-03 3.582790e-02 4.046705e-02
[37] 3.357067e-02 2.013851e-02 -6.054430e-03 -1.465642e-02 1.149496e-02 -5.667473e-02 -6.620702e-02 -8.065134e-02 -7.291942e-02
[46] -7.039425e-02 -8.032072e-02 -1.179327e-02 -7.080213e-03 -4.892581e-02 -5.723925e-02 -1.095635e-01 -1.193642e-01 -1.320603e-01
[55] -1.401216e-01 -1.356139e-01 -1.284428e-01 -1.290476e-01 -1.163493e-01 -1.287875e-01 -1.329666e-01 -8.598913e-02 -1.116608e-01
[64] -1.048289e-01 -1.051069e-01 -5.112310e-02 -3.134091e-02 -6.088656e-02 -6.101064e-02 -1.615522e-02 -1.021232e-02 2.703312e-02
[73] 4.954283e-02 4.315804e-02 2.719882e-02 3.670356e-02 4.422997e-02 5.011668e-02 4.552377e-02 5.688449e-02 3.507469e-02
[82] 3.391465e-02 6.444333e-02 8.011616e-02 8.157409e-02 4.583216e-02 1.691226e-02 -1.310009e-02 -6.253229e-03 -2.445900e-02
[91] -2.817816e-02 1.119052e-02 2.662970e-02 4.914242e-02 8.787654e-02 6.454450e-02 5.280729e-02 3.546875e-02 2.567525e-02
[100] 2.392683e-02
The PercChange function has now been deprecated and I need to find a new function to replace it. Essentially, I need a function that calculates the percent change of df$Close[1:20] (This would be Close of day 1 minus close of day 20, divided by close of day 20), then rolls to [2:21] for the next row, then [3:22],[4:23], and so on.
Thanks in advance!
CodePudding user response:
Here is a simple Base R solution:
PercChange<- function(x, slideBy){
-diff(x, slideBy)/ tail(x, -slideBy)
}
PercChange(df$Close, slideBy = 20)
[1] 5.695617e-02 2.422862e-02 3.920509e-02 -1.706445e-02
[5] -2.016308e-02 -1.997009e-02 -5.748624e-02 -1.446751e-02
[9] -1.722569e-02 -3.790530e-03 -3.976292e-02 -1.877438e-02
If you desire a datframe back, then modify this into:
PercChange<- function(data, Var, NewVar, slideBy){
x <- data[[Var]]
data[NewVar] <- c(-diff(x, slideBy)/ tail(x, -slideBy), numeric(slideBy))
data
}
PercChange(df, Var = 'Close', NewVar = 'OneMonthChange', slideBy = 20)
data:
df <- structure(list(Close = c(342.94, 346.22, 346.18, 335.24, 330.45,
334.2, 325.45, 333.79, 334.9, 341.66, 333.74, 334.49, 329.75,
329.82, 330.56, 322.81, 317.87, 306.84, 310.39, 310.6, 324.46,
338.03, 333.12, 341.06, 337.25, 341.01, 345.3, 338.69, 340.77,
342.96, 347.56, 340.89, 327.74, 327.64, 335.37, 338.62, 341.13,
335.85, 331.62, 328.08, 329.98, 323.57, 316.92, 312.22, 315.81,
328.69, 324.61, 341.88, 340.78, 339.99, 335.34, 324.76, 328.53,
324.54, 323.77, 325.45, 330.05, 329.22, 333.64, 332.96, 326.23,
343.01, 339.39, 339.61, 340.65, 353.58, 352.96, 345.96, 343.21,
357.48, 355.7, 364.72, 373.06, 373.92, 376.53, 376.51, 378.69,
378, 377.57, 382.18, 376.26, 375.28, 382.05, 379.38, 380.66,
372.63, 364.38, 368.39, 365.51, 363.35, 359.37, 355.12, 355.45,
358.45, 366.56, 363.18, 362.65, 359.96, 361.13, 361.61)), class = "data.frame", row.names = c(NA,
-100L))
CodePudding user response:
A tidyverse
approach
library(tidyr)
library(dplyr)
df %>% mutate(OneMonthChange=(Close-lead(Close, 20))/lead(Close, 20),
OneMonthChange=replace_na(OneMonthChange,0))
Close OneMonthChange
1 342.94 5.695617e-02
2 346.22 2.422862e-02
3 346.18 3.920509e-02
4 335.24 -1.706445e-02
5 330.45 -2.016308e-02
6 334.20 -1.997009e-02
etc...