braverock / PerformanceAnalytics

207 stars 105 forks source link

Return.excess returns 0-s only, if R and Rf have the same column names #187

Open nagbalae opened 4 months ago

nagbalae commented 4 months ago

Description

Return.excess, and other functions that use it, like Sharpe return 0-s only, if the R and Rf argument xts objects have the same column name.

Expected behavior

Return.excess returns the correct returns no mater the names of the passed objects

Minimal, reproducible example

library(PerformanceAnalytics)
# Create 2 different xts objects
A <- ts(rnorm(10),start = 1) |> as.xts()
B <- ts(rnorm(10),start = 1) |> as.xts()

colnames(A) <- "A"
colnames(B) <- "A"

Return.excess(A,B)
SharpeRatio(A,B)
# Returns all 0-s

# Same code, but with different names
colnames(B) <- "B"

Return.excess(A,B)
SharpeRatio(A,B)
# Returns correct results

Session Info

R version 4.3.2 (2023-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19045)

Matrix products: default

locale:
[1] LC_COLLATE=Hungarian_Hungary.utf8  LC_CTYPE=Hungarian_Hungary.utf8    LC_MONETARY=Hungarian_Hungary.utf8
[4] LC_NUMERIC=C                       LC_TIME=Hungarian_Hungary.utf8    

time zone: Europe/Budapest
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] PerformanceAnalytics_2.0.4 xts_0.13.2                 zoo_1.8-12                

loaded via a namespace (and not attached):
[1] compiler_4.3.2    tools_4.3.2       rstudioapi_0.15.0 grid_4.3.2        lattice_0.21-9   
[6] quadprog_1.5-8 
nagbalae commented 4 months ago

The problem is with the following lines:

Rft = cbind(R, Rf)
Rft = na.locf(Rft[, make.names(coln.Rf)])

If the two objects share the same name, the Rf gets ".1" at the end, and gets filtered out

A check should be added, for example:

if (colnames(R) == colnames(Rf)){
  colnames(Rf) = "Rf"
  coln.Rf = colnames(Rf)
}

or simply at the if function before the cbind

if (is.null(coln.Rf) | colnames(R) == colnames(Rf)) {
            colnames(Rf) = "Rf"
            coln.Rf = colnames(Rf)
        }

or in case this renaming to Rf is unwanted this should fix it as well

if (colnames(R) == colnames(Rf)){
  colnames(Rf) = paste0(coln.Rf,".1")
  coln.Rf = colnames(Rf)
}