Es posible que desee consultar la literatura sobre el índice de índices; tal vez encuentre ideas útiles en ella.
Lo escribiría y lo resolvería como un modelo de optimización de optimización. Dado que parece estar más interesado en el número de activos que se requieren para replicar estrechamente su cesta (es decir, la cardinalidad de la cesta de la cesta), podría resolver el modelo para diferentes cardinalidades y luego observar el equilibrio correlación/cardinalidad.
Permítanme esbozar cómo podría ser ese modelo de optimización en R, utilizando un método llamado Threshold Accepting (implementado en el NMOF
paquete). Utilizo la correlación, tal y como la has descrito, aunque la correlación puede no ser la mejor medida para la cercanía, ya que puede tener cestas que están altamente correlacionados pero con volatilidades muy diferentes.
n <- 30 ## number of assets in original basket
k <- 3 ## number of assets in replicating basket
La cesta original: Asumo pesos iguales, pero simplemente pero se pueden añadir otros pesos si se desea.
w.basket <- rep(1/n, n)
library("NMOF") ## https://github.com/enricoschumann/NMOF
library("neighbours") ## https://github.com/enricoschumann/neighbours
Empiezo creando unos rendimientos aleatorios para sus 30 activos. El resultado es una matriz R
de tamaño ns
veces na
.
random_returns <- function(na, ns, sd, mean = 0, rho = 0) {
## na = number of assets
## ns = number of scenarios
## sd = vol of returns
## mean = means of returns
## ==> sd and mean may be scalars or
## vectors of length na
ans <- rnorm(na*ns)
dim(ans) <- c(na, ns)
if (rho != 0) {
C <- array(rho, dim = c(na, na))
diag(C) <- 1
ans <- t(chol(C)) %*% ans
}
ans <- ans*sd
ans <- ans + mean
t(ans)
}
R <- random_returns(na = n, ns = 250, 0.01, rho = 0.1)
La aceptación del umbral es un método de optimización, por lo que necesitamos una función objetivo: la correlación entre su cesta y una cartera de réplica x
. Para simplificar, también utilizo pesos iguales para x
pero sólo para el k
activos incluidos. Todas las demás ponderaciones son cero.
cor_basket <- function(x, R, w.basket, k, ...)
-c(cor(R %*% (x/k), R %*% w.basket))
Lo mejor que se puede conseguir es una correlación de 1. Como seguimos la convención de minimizar, utilizamos menos la correlación.
cor_basket(w.basket, R, w.basket, k)
## [1] -1
A continuación, la parte clave de la aceptación de umbrales: la función de vecindad. La vecindad toma una solución, hace una copia, modifica la copia ligeramente (y al azar), y devuelve esta copia modificada. En el caso aquí, la función selecciona aleatoriamente un activo de la canasta y lo sustituye por un activo que no estaba en la cesta.
nb <- neighbourfun(type = "logical",
kmin = k,
kmax = k)
x0 <- c(rep(TRUE, k), rep(FALSE, n - k))
data.frame(x0, n1 = nb(x0), n2 = nb(x0))
## x0 n1 n2
## 1 TRUE TRUE FALSE
## 2 TRUE FALSE TRUE
## 3 TRUE TRUE TRUE
## 4 FALSE FALSE FALSE
## 5 FALSE FALSE FALSE
## 6 FALSE FALSE FALSE
## 7 FALSE FALSE FALSE
## [...]
## 12 FALSE TRUE FALSE
## [...]
## 23 FALSE FALSE TRUE
## [...]
sol.ls <- TAopt(cor_basket,
list(x0 = x0,
nI = 5000,
neighbour = nb),
R = R,
k = k,
w.basket = w.basket)
## Threshold Accepting
## [...]
## Best solution overall: -0.6534397
Recordemos que hemos minimizado: así que la mayor correlación con una cartera de seguimiento de tres activos es de 0,65. Ahora, para ver cómo es la relación correlación/número de activos, basta con ejecutar un bucle.
for (k.i in 2:20) {
x0 <- c(rep(TRUE, k.i), rep(FALSE, n - k.i))
sol.ls <- TAopt(cor_basket,
list(x0 = x0,
nI = 5000,
neighbour = nb,
printDetail= FALSE,
printBar = FALSE),
R = R,
k = k.i,
w.basket = w.basket)
message(format(k.i, width = 3),
" | ",
round(-sol.ls$OFvalue, 3))
}
## 2 | 0.566
## 3 | 0.653
## 4 | 0.721
## 5 | 0.765
## 6 | 0.8
## 7 | 0.833
## 8 | 0.86
## 9 | 0.876
## 10 | 0.89
## 11 | 0.9
## 12 | 0.913
## 13 | 0.921
## 14 | 0.931
## 15 | 0.938
## 16 | 0.946
## 17 | 0.95
## 18 | 0.955
## 19 | 0.96
## 20 | 0.965