4 votos

Alternativas de optimización CVAR

¿Existen alternativas a la medida CVaR para la optimización de carteras, que sean más fáciles de implementar, por ejemplo, con un programa lineal? Pueden ser sólo aproximaciones del CVaR o medidas que representen conceptos similares.

Editar : Me gustaría prefiere algunos métodos exactos (analíticos) en lugar de (meta-)heurísticos, ya que parecen ser bastante sensibles a los cambios (para mi caso). Si no es así, también son bienvenidos otros enfoques para que la heurística sea más fiable/robusta.

Edición2 : Si no hay aproximaciones reales a la CVaR, o si se puede proporcionar una explicación más detallada (de todos los pasos) de la optimización de la CVaR, eso también está en el tema, ya que eso nos permitiría utilizar métodos estándar y analíticos (como LP).

2 votos

IIRC, CVaR se puede implementar como un programa lineal. Busque 'Optimization of conditional value-at-risk' de Rockafellar/Uryasev. Pero, en general, hay que diferenciar entre el modelo que se quiere resolver (en su caso, la función objetivo específica) y los métodos para resolver el modelo. Puede haber varios métodos, y algunos son más sencillos/rápidos/lo que sea que otros.

0 votos

@EnricoSchumann Sí, CVaR se puede implementar como un programa lineal, sin embargo no he sido capaz de hacerlo porque el modelo matemático subyacente es demasiado complejo para mí, por eso estaba buscando alternativas o aproximaciones. Busco encontrar el óptimo global de alguna función objetivo, de ahí que sugiriera la programación lineal, ya que sospecho que probablemente implique esto. Pero si no es así, otras soluciones también son bienvenidas.

0 votos

Se podría abordar esencialmente cualquier modelo con heurística. Estos métodos son muy sencillos, pero pueden requerir algo de programación. Hemos utilizado un único método, el Umbral de Aceptación, para resolver todo tipo de modelos. Véase, por ejemplo papers.ssrn.com/sol3/papers.cfm?abstract_id=1365167 para diferentes modelos; y papers.ssrn.com/sol3/papers.cfm?abstract_id=2551745 para el tipo de métodos.

5voto

BigCanOfTuna Puntos 210

Siguiendo los comentarios y las ediciones de la pregunta, intentaré mostrar cómo el El valor en riesgo condicional (también conocido como pérdida de cola esperada) puede ser minimizado para una cartera. Comenzamos con la implementación sugerida por Rockafellar/Uryasev:

@ARTICLE{,  
  author  = {R. Tyrrell Rockafellar and Stanislav Uryasev},
  title   = {Optimization of Conditional Value-at-Risk},
  journal = {Journal of Risk},
  year    = 2000,
  volume  = 2,
  number  = 3,
  pages   = {21--41},
  doi     = {10.21314/JOR.2000.038}

}

Su descripción del modelo como programa lineal (LP) se da después de la ecuación 17 en su documento. Nosotros comenzamos con un conjunto de escenarios, es decir, una muestra de posibles de posibles realizaciones de la rentabilidad. Obsérvese que en el documento Rockafellar/Uryasev trabajan con pérdidas, es decir, con rendimientos negativos. Por lo tanto, un cuantil "malo" es uno alto, por ejemplo el 90º. El cuantil elegido se denomina $\beta$ en el papel. Almacenamos los escenarios en una matriz $R$ . En tiene $n_{\mathrm{A}}$ columnas (una para cada activo) y $n_{\mathrm{S}}$ filas (una fila para cada escenario).

Las variables del modelo son las ponderaciones reales de la cartera pesos de la cartera $x$ más las variables auxiliares $u$ (uno para cada escenario), más el nivel de VaR $\alpha$ . El solucionador puede elegir el nivel de VaR y las ponderaciones de forma que el CVaR se minimice. La función objetivo sólo tiene variables auxiliares y el nivel de VaR en ella; las ponderaciones reales de la cartera entran en el modelo sólo a través de las restricciones.

Los pesos de la función objetivo son los siguientes:

\begin {array}{ccccc} \underbrace { \begin {matriz} \phantom {000}1 \phantom {000} \end {matrix}}_{ \alpha }& \underbrace { \begin {matriz}0 & \cdots & 0 \phantom {0} \end {matrix}}_{x} & \underbrace { \begin {matriz} \frac {1}{(1- \beta ) n_{ \mathrm {S}}} & \cdots & \frac {1}{(1- \beta ) n_{ \mathrm {S}}} \end {matrix}}_{u} \\ \end {array}

Es decir, un vector de un 1, seguido de $n_{\mathrm{A}}$ ceros, y luego $n_{\mathrm{S}}$ por una constante.

La matriz de restricciones tiene el siguiente aspecto:

\begin {array}{ccccccc} 0 & 1 & \cdots & 1 & 0 & 0 & \cdots & 0 & = &1 \\ 1 & r_{1,1} & \cdots & r_{1, n_ \mathrm {A}} & 1 & 0 & \cdots & 0 & \geq & 0 \\ 1 & r_{2,1} & \cdots & r_{2, n_ \mathrm {A}} & 0 & 1 & \cdots & 0 & \geq & 0 \\ \vdots \\ \underbrace { \phantom {00}1 \phantom {00}}_{ \alpha } & \underbrace { \phantom {00}r_{n_{ \mathrm {S}},1} \phantom {00}}_{x_1} & \cdots & \underbrace {r_{n_ \mathrm {S}, n_ \mathrm {A}}}_{x_{n_} \mathrm {A}}} & \underbrace {0}_{u_1} & \underbrace {0}_{u_2} & \cdots & \underbrace {1}_{u_{n_n_} \mathrm {S}}} & \geq & 0 \\ \end {array}

Obsérvese que la primera línea es la restricción presupuestaria presupuesto. Aparte de esa línea, la matriz consta de una columna de unos (para la variable VaR), la matriz de escenarios matriz $R$ y una matriz de identidad de dimensión $n_\mathrm{S}$ . Existen restricciones de no negatividad para todos los $x$ y $u$ pero no se muestran explícitamente explícitamente. Muchos solucionadores los aplican automáticamente.

Podemos probar una implementación en R. Cargamos los paquetes paquetes necesarios para los ejemplos.

library("Rglpk")
library("NMOF")        ## https://github.com/enricoschumann/NMOF
library("neighbours")  ## https://github.com/enricoschumann/neighbours

(Revelación: soy el encargado de mantener los paquetes NMOF y neighbours .)

Comenzamos con un pequeño conjunto de datos, para poder observar la función objetivo y la matriz de restricciones: sólo 3 activos y 10 escenarios.

ns <- 10  ## number of scenarios
na <- 3   ## number of assets
R <- randomReturns(na, ns, sd = 0.01)  ## an array of size ns x na
b <- 0.8  ## beta in the original paper

La función objetivo da una ponderación nula a los x .

f.obj <- c(alpha = 1,
           x = rep(0, na),
           u = 1/rep((1 - b)*ns, ns))
f.obj

## alpha   x1    x2    x3    u1    u2    u3    ...    u9   u10 
##   1.0  0.0   0.0   0.0   0.5   0.5   0.5    ...   0.5   0.5

La matriz de restricciones gana una columna para cada escenario.

C <- cbind(1, R, diag(nrow(R)))
C <- rbind(c(alpha = 0, x = rep(1, na), u = rep(0, nrow(R))), C)
C

      alpha        x1        x2       x3 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10
 [1,]     0  1.000000  1.000000  1.00000  0  0  0  0  0  0  0  0  0   0
 [2,]     1  0.000183  0.029174  0.00293  1  0  0  0  0  0  0  0  0   0
 [3,]     1 -0.001776 -0.001673  0.00225  0  1  0  0  0  0  0  0  0   0
 [4,]     1 -0.009948 -0.007892  0.01129  0  0  1  0  0  0  0  0  0   0
 [5,]     1  0.008299 -0.005601 -0.00144  0  0  0  1  0  0  0  0  0   0
 [6,]     1  0.005766  0.000521 -0.00940  0  0  0  0  1  0  0  0  0   0
 [7,]     1 -0.017110  0.016782 -0.00122  0  0  0  0  0  1  0  0  0   0
 [8,]     1 -0.008334  0.017317  0.00498  0  0  0  0  0  0  1  0  0   0
 [9,]     1 -0.004077 -0.009600  0.01568  0  0  0  0  0  0  0  1  0   0
[10,]     1 -0.000532 -0.000201  0.00267  0  0  0  0  0  0  0  0  1   0
[11,]     1 -0.005090  0.002318  0.00368  0  0  0  0  0  0  0  0  0   1

Vamos a ejecutarlo en un modelo más grande.

ns <- 5000
na <- 20
R <- randomReturns(na, ns, sd = 0.01, rho = 0.5)
b <- 0.75

f.obj <- c(alpha = 1,
           x = rep(0, na),
           u = 1/rep(( 1 - b)*ns, ns))

C <- cbind(1, R, diag(nrow(R)))
C <- rbind(c(alpha = 0, x = rep(1, na), u = rep(0, nrow(R))), C)

const.dir <- c("==", rep(">=", nrow(C) - 1))
const.rhs <- c(1, rep(0, nrow(C) - 1))

sol.lp <- Rglpk_solve_LP(f.obj,
                         C,
                         const.dir,
                         rhs = const.rhs,
                         control = list(verbose = TRUE, presolve = TRUE))
## GLPK Simplex Optimizer, v4.65
## 5001 rows, 5021 columns, 110020 non-zeros
## ...
## OPTIMAL LP SOLUTION FOUND

Almacenamos los pesos resultantes en una variable lp.weights .

lp.weights <- sol.lp$solution[2:(1+na)]

Ahora vamos a resolver el mismo modelo con una heurística. Podemos podemos utilizar uno de los métodos más sencillos, una Búsqueda local. Y también dejaré la implementación aquí simple (podría ser mejorada para ganar velocidad).

La búsqueda local es sencilla: partimos de una cartera, por ejemplo, una con pesos iguales para todos los activos. A continuación, evaluamos esta cartera. buena es. Así que escribimos una función objetivo que que, dados los datos, asigna una cartera a un número - el CVaR.

CVaR <- function(w, R, b) {
      Rw <- R %*% w   ## compute portfolio loss under scenarios
      mean(Rw[Rw >= quantile(Rw, b)])
}

Y ahora ejecutamos la heurística: hará un bucle a través del espacio de posibles carteras y aceptará las carteras que son mejores que la actual, pero rechaza aquellas carteras que son peores. Para este bucle, necesitamos un segundo ingrediente (el primero era la función objetivo): la función de vecindad. La función de vecindad función de vecindad toma una cartera como entrada y devuelve una copia ligeramente modificada de esta cartera.

nb <- neighbourfun(0, 1,
                   type = "numeric",
                   stepsize = 0.05)

Para dar una idea, supongamos que tenemos una cartera formada por sólo tres activos, y que cada activo tiene un peso de un tercio. Entonces podríamos calcular los vecinos de la siguiente manera:

nb(rep(1/3, 3))
## 0.3122 0.3544 0.3333

nb(rep(1/3, 3))
## [1] 0.3272 0.3333 0.3394

Ahora que tenemos un objetivo y una función de vecindad, podemos ejecutar la búsqueda local.

sol.ls <- LSopt(CVaR,
                list(x0 = rep(1/na, na),
                neighbour = nb,
                nI = 1000),
                R = R, b = b)

Podemos comparar los valores de la función objetivo. Obsérvese que las definiciones de la función objetivo no son exactamente equivalentes, ya que el LP puede elegir el VaR y las pesos juntos, mientras que la Búsqueda Local sólo varía las ponderaciones y luego, en la función objetivo impone una forma de calcular el VaR.

CVaR(sol.ls$xbest, R, b)
CVaR(lp.weights, R, b)

## [1] 0.00946
## [1] 0.00955

Así que ambas implementaciones dan resultados muy similares.

En cuanto a la inestabilidad: Podemos ejecutar la heurística varias veces (siempre deberíamos hacerlo; véase http://enricoschumann.net/R/remarks.htm ). Vamos a ejecutarlo 20 veces.

sols.ls <- restartOpt(LSopt,
                      n = 20,
                      OF = CVaR, 
                      list(x0 = rep(1/na, na),
                           neighbour = nb,
                           nI = 1000),
                      R = R, b = b)
summary(sapply(sols.ls, `[[`, "OFvalue"))

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.00946 0.00946 0.00946 0.00946 0.00946 0.00946

Así que obtenemos resultados muy similares en todas las ejecuciones.

Lo bueno de la heurística es que podemos fácilmente cambiarla. Supongamos que ya no queremos el CVaR; pero ahora preferimos minimizar un momento parcial, digamos. Entonces todo lo que tendríamos que hacer es escribir otra función objetivo objetivo.

PM <- function(w, R, exp = 2, ...) {
      Rw <- R %*% w   ## compute portfolio loss under scenarios
      pm(Rw, xp = exp, lower = FALSE)  ## we work with losses
}
sol.ls <- LSopt(PM,
                list(x0 = rep(1/na, na),
                     neighbour = nb,
                     nI = 1000),
                R = R,
                exp = 2)

0 votos

Este ha sido, con diferencia, el ejemplo más sencillo y fácil de entender sobre el tema que he encontrado. Muy bueno.

1 votos

Actualización: La versión de desarrollo del NMOF dispone ahora de una función minCVaR que aplica el enfoque LP.

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X