What is the best method for calculating the EWMA returns for every column of my time series? Above all columns we have the returns from Today - 260d (-1Year) until Today -1d.
**The returns are calculated by the division of the close prices between days.
I was using the following function:
ewma.func <- function(rets, lambda) {
sig.p <- 0
sig.s <- vapply(rets, function(r) sig.p <<- sig.p*lambda + (r^2)*(1 - lambda), 0)
return(sqrt(sig.s))
}
but It can only generate the Ewma for 1 column per time, so I also have to do the following:
ewma_col = NULL
for (w in 1:ncol(df)){
ewma_col[[w]] = ewma.func(df[,w], lambda = 0.94)
}
df2 <- do.call(rbind, ewma_col) %>% t()
colnames(df2) = colnames(df)
Since I have 5 columns from this particular object and have more other 100 objects like these inside a list I'm working with, it becomes very hard and unefficient to have to calculate the Ewma for every column for every object. So I was thinking if there is a simpler way of doing that.
My sampled df:
structure(list(`25079578000106` = c(0.311405806132825, 0.0261260831393884,
0.126801611077099, -0.201990496952931, -0.169037712385034, -0.372023939507926,
0.426906935535953, -0.402262040825008, -0.273008284875687, 0.142923301064002,
0.0522466965776403, 0.491128923749784, 0.547432459279662, -0.00905547394722817,
0.243408062669914, -0.565142654522788, -0.0284871479379945, 0.141976900522423,
-0.115634388475883, 0.0858369759953348, 0.252102295598888, -0.130994651044603,
0.213179273123387, 0, 0.254748840234242, -0.162688137697842,
0.0670642675686395, 0.409574624973175, 0.11580733826122, 0.152815408000606,
-0.194192341950838, 0.079688931509736, 0.0390181277907686, 0.0366672406016733,
-0.0841513321574894, 0.170703395997407, -0.1032803445014, 0.301935098286776,
0.12983982123842, 0.179888841921638, -0.04270641511539, 0.194911670405418,
-0.126730582360324, 0.348033349109755, 0.0962079717282904, 0.0734806822947576,
0.151055897003971, -0.0701511527950061, -0.161361593563925, 0.246798639636836
), `21144577000147` = c(0.402627056610072, 0.0670045021252008,
0.136672287590045, -0.257998532470083, -0.126993350295379, -0.57979580369647,
0.493768537307915, -0.491292521383002, -0.403311319223576, 0.130267872918921,
-0.0309827290038811, 0.617972996951721, 0.486863606965926, 0.0791557540651411,
0.221599948054063, -0.743017289278214, -0.122417766579019, 0.199045961198863,
-0.204796549951425, 0.0958513541263528, 0.221446985779039, -0.103656955252518,
0.242373424043762, 0, 0.320491723141458, -0.187373789685807,
0.073898113987525, 0.443193321189028, 0.131922088075953, 0.167945069370035,
-0.218753095850843, 0.0856883381857187, 0.0915706430532737, 0.0253365722528542,
-0.10242040234516, 0.210685512865894, -0.111825193016557, 0.343926238201675,
0.145042635631398, 0.169826889032265, -0.085688805575046, 0.256890913078678,
-0.173078901207191, 0.502885210153181, 0.0139494439281407, 0.111911786007113,
0.124141056221561, -0.1009381527183, -0.164678661440121, 0.270671359612606
), `19107923000175` = c(1.17081038442848, -1.53767897591024,
-0.511278352678346, 0.801980435971927, -1.1354756311448, 1.33550018854294,
-0.877121115991031, 0.893385693962045, -3.05784205729651, 0.790948188478069,
-0.874211667633062, -2.0517918994301, 0.108547761010414, 1.31951493240194,
0.59011726098106, 0.751824284998293, -2.542040795106, -1.30722252988562,
0.166101507966232, -0.333577277251607, -0.48391700402135, -0.287302340893802,
0.276978237343428, 0, -2.20114477424431, 2.28636453339277, 3.21842714220111,
0.591201915267447, 1.88892838687025, -2.4835963874466, 0.93808037963754,
-2.02373054462441, 1.10818007306079, 0.963590860919794, 0.221162120942608,
0.927865234370984, 1.30669520840456, -1.5475129142942, 1.44346553624928,
-1.33299447861646, 2.56613694509724, -0.854390492077073, 0.431278918404132,
-0.419447091917391, 0.437028634769376, -0.279096110807586, 0.702864309823781,
1.8092529326168, -1.76575759915067, 1.79323091451806), `25079578000106` = c(-1.46258859240334,
-1.08758898677479, -0.0989607635347056, 0.877778709582344, -1.81190225830505,
3.65239411476068, -0.591252178764989, 1.21883593492385, 2.9361510378294,
-1.21526156190157, 5.60858230674057, -0.483417673513031, 3.11737542488117,
-0.928573480450723, -0.855911339203885, 1.42741011768521, 2.48564664470905,
3.64030099535739, -0.0133031404402573, 1.84565666459093, 3.33521612974437,
-0.706821796120494, -1.41998375802359, 0, -1.00702592444577,
-0.764259576953918, 0.504494091364904, 2.34908743768756, 1.12513038984616,
0.883990707916382, -0.23625019375686, 0.794114018390246, -1.84599011799946,
1.00693676176888, -2.68018999058768, 2.1352680909331, -0.361733150930377,
1.57261038511933, 0.0516994778081425, 1.29365618286101, 1.84691599060898,
-0.271832695671037, 1.894436301518, 0.0966644805885153, 1.10278020638361,
-1.48991306559765, 0.533713807271852, 0.703722278376517, -0.931114916329534,
2.53580948592571), `19436835000117` = c(1.49069022500044, -1.29966042904925,
-0.395616604691895, 0.727380076932604, -0.30439719239439, 0.550924036724609,
-0.846017086223583, 0.841084288731508, -2.71310085681762, 0.432345969238668,
-1.42297721340583, -1.75329706107732, -0.234704765443894, 1.02912636612018,
0.953879318876716, 0.506016590225045, -2.46852979989853, -1.29307204251745,
-0.361195165078243, 0.142310472620011, -0.545533438798884, 0.0622563582510338,
0.664697968204564, 0, -2.65178033678239, 1.65225289310911, 2.5845850508631,
0.743106457593967, 1.91502897378086, -2.12601029097641, 0.531378326195409,
-1.64881667524241, 0.658820966236817, 0.782823536428623, -0.430202234929311,
0.941061544290278, 1.38020377507928, -1.04732682539179, 0.918463659036206,
-0.891537194911507, 2.72019066906068, -0.480601724302687, 0.65309472320223,
0.334795709022728, 0.0443713630374987, -0.747195361418562, 0.921720304359042,
1.04346702937619, -1.57727738560425, 1.28708233642101)), row.names = c("Retorno D - 260",
"Retorno D - 259", "Retorno D - 258", "Retorno D - 257", "Retorno D - 256",
"Retorno D - 255", "Retorno D - 254", "Retorno D - 253", "Retorno D - 252",
"Retorno D - 251", "Retorno D - 250", "Retorno D - 249", "Retorno D - 248",
"Retorno D - 247", "Retorno D - 246", "Retorno D - 245", "Retorno D - 244",
"Retorno D - 243", "Retorno D - 242", "Retorno D - 241", "Retorno D - 240",
"Retorno D - 239", "Retorno D - 238", "Retorno D - 237", "Retorno D - 236",
"Retorno D - 235", "Retorno D - 234", "Retorno D - 233", "Retorno D - 232",
"Retorno D - 231", "Retorno D - 230", "Retorno D - 229", "Retorno D - 228",
"Retorno D - 227", "Retorno D - 226", "Retorno D - 225", "Retorno D - 224",
"Retorno D - 223", "Retorno D - 222", "Retorno D - 221", "Retorno D - 220",
"Retorno D - 219", "Retorno D - 218", "Retorno D - 217", "Retorno D - 216",
"Retorno D - 215", "Retorno D - 214", "Retorno D - 213", "Retorno D - 212",
"Retorno D - 211"), class = "data.frame")
Avoiding the assignment that ignores local scoping <<-
is desirable. That little assignment can get missed when refactoring or copy/pasting code.
Copy pasting the dput
output above into a text file allows us to source the file and store the value, a data.frame, in a variable (df
).
DPUT_TEXT_FILE <- '/tmp/example_dput.txt'
df <- source(DPUT_TEXT_FILE)$value
We'll use purrr
to create a function on the fly, and dplyr
will get used to apply a function to every column of the data frame.
library(dplyr)
library(purrr)
The innermost calculation in the question's body boils down to the following. An important aspect of the original implementation that this eschews (for now) is that the result of this function gets fed back into subsequent calls.
# Calculate single statistic
single_ewa <- function(sig_p, val, lambda){
sig_p*lambda + (val^2)*(1 - lambda)
}
# Calculate full weighted moving average
calc_ewma <- function(vals, lambda){
# Partially apply calc single stat function to set lambda
part_ewa <- partial(single_ewa, lambda=lambda)
# Reduce and accumulate to get the raw moving results
raw_result <- Reduce(f=part_ewa, x=vals, init=0, accumulate = TRUE )
# Square root finishes the calculation
result <- sqrt(raw_result)
# Finally, drop the initial condition from the accumulation
result <- result[2:length(result)]
return(result)
}
Using purrr::partial
we can set the lambda and get a function returned that no longer needs us to pass it as a parameter. This is also referred to as currying a function. It gets a function with the airity (number of parameters) we're looking for. Namely, the accumulated value sig_p
and a new value val
.
Reduce
We're using Reduce
to start with an initial value of 0, pass it a value from the input vector, run the function that processes those two, and finally accumulate the result for the next iteration. This continues once for each member of the input vector. Using accumulate=TRUE
yields a vector of the accumulated values instead of just the final value.
Temporarily swapping out column names for enumerated names avoids the dplyr
functions from throwing errors about non-unique column names.
# Save old colnames as hack around duplicate column names
old_colnames <- colnames(df)
colnames(df) <- as.character(1:ncol(df))
# Do calculation
ewma_df <- df %>% mutate_all(.funs=calc_ewma, lambda=0.94)
# re-assign colnames
colnames(df) <- old_colnames
colnames(ewma_df) <- old_colnames