如何基于含NA值的季度收益率序列计算年化收益率?
Hey Roger, let's break down why your rolling annualized return calculations aren't matching what you expected, especially with those NA values cluttering up your quarterly return data.
The Core Issue
Your current code uses rollapply() with Return.annualized() from the PerformanceAnalytics package, but the default behavior doesn't handle NA values the way you probably want it to. When a rolling window includes NAs:
Return.annualized()either ignores them entirely (if you setna.rm=TRUE) but still uses a fixedscale=4(assuming 4 full quarters), which skews the result- Or it returns NA (if
na.rm=FALSE), which might not be what you're after either
Let's Fix This
We'll create custom functions to handle NAs properly, depending on your exact needs.
First, let's recap your sample data and original code for context:
library(zoo) library(PerformanceAnalytics) # Your sample data df <- data.frame(v1 = c(0.01, 0.03, 0.02,0.05, 0.06,0.03,0.01, NA), V2 = c(NA, 0.1, 0.02,NA,0.03,0.05,0.11, 0.2)) # Your original code df_annual <- rollapply(df, width = 4, Return.annualized, scale =4, align = "right", fill = NA)
Option 1: Calculate Annualized Returns Using Valid Quarters in the Window
If you want to compute the return using whatever valid (non-NA) quarters are present in the 4-quarter window, then scale it to annualized based on the number of valid periods:
# Custom function to handle NAs by using valid returns and scaling appropriately calc_annualized_with_na <- function(x) { # Filter out NA values from the window valid_returns <- x[!is.na(x)] num_valid <- length(valid_returns) # Return NA if there are no valid returns in the window if (num_valid == 0) return(NA) # Calculate geometric mean return, then annualize based on valid quarters total_growth <- prod(1 + valid_returns) annualized_return <- total_growth^(4 / num_valid) - 1 return(annualized_return) } # Apply the custom function to your data df_annual_adjusted <- rollapply(df, width = 4, FUN = calc_annualized_with_na, align = "right", fill = NA)
For example, take the 4th row of your V2 column (window: NA, 0.1, 0.02, NA):
- Valid returns are
0.1and0.02(2 quarters) - Calculation:
(1.1 * 1.02)^(4/2) - 1 = 1.258884 - 1 = 0.258884(matches your original result, but this logic explicitly accounts for the missing quarters)
Option 2: Only Calculate Returns for Full, Non-NA Windows
If you only want annualized returns when a 4-quarter window has no NA values (ignoring partial windows), use this stricter function:
# Custom function that only computes returns for complete, non-NA windows calc_annualized_full_window <- function(x) { # Return NA if any value in the window is NA if (any(is.na(x))) return(NA) # Use standard annualization for a full 4-quarter window Return.annualized(x, scale = 4) } # Apply to your data df_annual_full <- rollapply(df, width = 4, FUN = calc_annualized_full_window, align = "right", fill = NA)
This will return NA for any window that includes even one NA value, which might be what you expected if you only wanted full-year calculations.
Wrap-Up
Pick the option that aligns with your business logic:
- Use Option 1 if you want to make use of partial valid data in each window
- Use Option 2 if you only trust full, complete 4-quarter periods
内容的提问来源于stack exchange,提问作者Roger

