Criterio SR para inclusión de nuevos activos en portafolios

Author

Julián Mauricio Fajardo, MsC en Actuaría y Finanzas

Published

December 19, 2024

Introducción

En este análisis, implementamos el Criterio SR (Sharpe Ratio) para la administración de portafolios de inversión riesgosos. Este criterio nos ayudará a decidir si es conveniente agregar un nuevo activo a un portafolio existente, en función de su impacto en el rendimiento y el riesgo del portafolio.

Para realizar este análisis, utilizamos los siguientes paquetes:

  • PerformanceAnalytics: para calcular métricas financieras como el Sharpe Ratio.
  • quantmod: para descargar datos financieros.
  • xts y zoo: para manejar series temporales de manera eficiente.
Code
library(PerformanceAnalytics)
library(quantmod)
library(zoo)
library(xts)

Definición de los Símbolos Financieros

El primer paso es definir los activos que se utilizarán para el análisis. En este caso, tomamos índices bursátiles asiáticos como Nikkei 225, Hang Seng y KOSPI. Hagamos un pequeño resumen de lo que representan cada uno de estos indicadores de desmpeño a nivel regional asia.

  1. Nikkei 225 El Nikkei 225 es el principal índice bursátil de Japón, compuesto por 225 de las empresas más grandes y representativas que cotizan en la Bolsa de Tokio. Este índice es considerado un barómetro clave para evaluar el rendimiento del mercado financiero japonés y refleja sectores como tecnología, manufactura y bienes de consumo.

  2. Hang Seng El Hang Seng es el índice de referencia de la Bolsa de Hong Kong. Incluye las principales empresas que cotizan en este mercado, muchas de las cuales tienen vínculos estrechos con China continental. El índice proporciona una visión general de la economía de Hong Kong y su integración con la economía china.

  3. KOSPI El KOSPI (Korea Composite Stock Price Index) es el principal indicador del mercado de valores de Corea del Sur. Este índice refleja el rendimiento de todas las empresas que cotizan en la Bolsa de Corea (Korea Exchange) y es ampliamente utilizado para medir la salud económica del país, con un enfoque en sectores como tecnología, automotriz y electrónica.

Code
# Definir los símbolos de los activos financieros
simbolos <- c("^N225", "^HSI", "^KS11") 
getSymbols(simbolos, # lista de los tickers de los activos 
           from="2024-01-01", # fecha inicial, si no se incluye fecha final es la mas reciente
           periodicity = "daily" # descarga precios diarios
           )
[1] "N225" "HSI"  "KS11"
Code
# Limpiar los nombres de los símbolos y extraer precios de cierre
list_clean <- lapply(gsub("\\^", "", simbolos), function(x) Cl(get(x)))
activos <- do.call(merge, list_clean)
activos2 <- na.omit(na.locf(activos))

Usaremos en este caso el histórico de los precios de los índices ya listados desde el primer dia bursátil del año 2024.

En el caso de los indices regionales, los cuales usan el símbolo “^” para su identificación en Yahoo Finance y algunas veces presentan problemas de la identificación del simbolos especiales. A continuación, se detalla la explicación del código construido:

  1. lapply(gsub("\\^", "", simbolos), function(x) Cl(get(x)))

Paso 1: gsub("\\^", "", simbolos)

  • Función gsub: Busca y reemplaza caracteres dentro de una cadena.
  • "\\^": Busca el símbolo ^ en las cadenas del vector simbolos. El doble backslash \\ es necesario porque ^ es un carácter especial en expresiones regulares.
  • "": Reemplaza el ^ por una cadena vacía (elimina el símbolo).
  • Resultado: Obtiene una lista de cadenas del vector simbolos donde se ha eliminado el símbolo ^.

Paso 2: lapply(..., function(x) Cl(get(x)))

  • lapply: Aplica una función a cada elemento de la lista resultante de gsub.
  • get(x): Toma el nombre del símbolo como texto y lo interpreta como el nombre de un objeto en el entorno de trabajo. Esto permite recuperar datos financieros asociados a cada símbolo.
  • Cl(get(x)):
    • Cl: Extrae los precios de cierre de un objeto que contiene datos financieros (como objetos de clases xts o zoo, típicamente obtenidos de paquetes financieros como quantmod).
    • Aplica esta operación a cada símbolo (o conjunto de datos) obtenido.

Resultado final de esta línea:

Crea una lista (list_clean) donde cada elemento contiene los precios de cierre de los datos financieros correspondientes a los símbolos dados, con el ^ eliminado.


  1. activos <- do.call(merge, list_clean)

Función do.call

  • Permite aplicar una función (merge en este caso) a los elementos de una lista como si fueran argumentos individuales.
  • merge: Combina múltiples series de tiempo en un único objeto.
  • list_clean: Es la lista de precios de cierre generada en el paso anterior.

Resultado:

Crea un único objeto (activos), generalmente un xts o zoo, que contiene los precios de cierre de todos los símbolos, combinados por sus fechas.


  1. activos2 <- na.omit(na.locf(activos))

na.locf(activos)

  • na.locf: Significa “Last Observation Carried Forward”.
  • Rellena los valores NA (valores faltantes) en las series de tiempo usando el último valor conocido. Esto es útil para manejar fechas donde no todos los activos tienen datos (como días festivos en ciertas bolsas).

na.omit(...)

  • Elimina filas que contienen valores NA después de aplicar na.locf.
  • Asegura que el objeto final no tenga valores faltantes.

Resultado final:

Crea un nuevo objeto (activos2) que es una versión limpia de activos, sin valores NA, y con datos de cierre coherentes para todas las fechas disponibles.


Utilizamos la función Return.calculate() para convertir los precios de cierre en retornos logarítmicos, y posteriormente ajustamos las series.

Code
retornos <- Return.calculate(activos2) # calculamos los retornos diarios
retornos <- retornos[-1,]
colnames(retornos) <- c("rNikkei225", "rHangSeng", "rKOSPI")
head(retornos)
            rNikkei225    rHangSeng        rKOSPI
2024-01-05 0.002677603 -0.006647274 -0.0034556908
2024-01-08 0.000000000 -0.018800948 -0.0039797095
2024-01-09 0.011557448 -0.002122147 -0.0025625153
2024-01-10 0.020097013 -0.005728175 -0.0075197989
2024-01-11 0.017657093  0.012720147 -0.0006726886
2024-01-12 0.015042856 -0.003524710 -0.0059914775

Criterio SR:

Presentaremos brevemente la teoria matemática sobre la cual se basa el ya mencionado criterio. Usaremos la teoria de las variables aleatorias para justificar esta parte. Recuerde las propiedades de la varianza y la covarianza de las variables alaetorias para iniciar la lectura y análisis de lo que sigue:

Para un portafolio con pesos arbitrarios, la varianza del portafolio está dada por: \[ \text{Var}[R_P] = \text{Cov}[R_P, R_P] = \text{Cov}\left[\sum_{i=1}^n x_i R_i, R_P \right] \]

Expandiendo la covarianza, obtenemos: \[ \text{Var}[R_P] = \sum_{i=1}^n x_i \cdot \text{Cov}[R_i, R_P] = \sum_{i=1}^n x_i \cdot \sigma_i \cdot \sigma_P \cdot \rho_{i,P} \]

Dividiendo ambos lados por \(\sigma_P\), resulta: \[ \sigma_P = \sum_{i=1}^{n} x_i \cdot \sigma_i \cdot \rho_{i,P} \]

Esta ecuación indica que cada activo contribuye a la volatilidad del portafolio en función de su riesgo total, ajustado por su correlación con el portafolio. La correlación mide qué fracción del riesgo total del activo es común al portafolio.

Mientras menor sea la correlación entre el activo y el portafolio, menor será el riesgo del portafolio.

En particular, si la correlación no es igual a (+1), la volatilidad del portafolio será estrictamente menor que el promedio ponderado de las volatilidades de los activos: \[ \sigma_P < \sum_{i=1}^n x_i \cdot \sigma_i \]

Esto demuestra que la volatilidad puede reducirse a través de la diversificación. Vamos a discutir cómo determinar si añadir una nueva inversión mejorará un portafolio. Un portafolio se considera mejorado si el índice de Sharpe aumenta.

Supongamos que tenemos un portafolio, denotado como \(P\), con un retorno esperado ( \(E[R_P]\)) y una volatilidad ( \(\sigma_P\)). Para determinar si agregar una nueva inversión mejorará el portafolio, consideramos tres factores:

  • El índice de Sharpe de la nueva inversión.

  • El índice de Sharpe del portafolio existente.

  • La correlación entre los rendimientos de la nueva inversión y los rendimientos del portafolio existente.

La regla es que debemos añadir la nueva inversión al portafolio si se satisface la siguiente condición:

\[\frac{E[R_{\text{Nuevo}}] - r_f}{\sigma_{\text{Nuevo}}} > \rho_{\text{Nuevo},P} \cdot \frac{E[R_P] - r_f}{\sigma_P} \]

Esto significa que si el índice de Sharpe de la nueva inversión es mayor que el producto del índice de Sharpe del portafolio existente y la correlación entre los rendimientos de la nueva inversión y el portafolio existente, entonces el portafolio se mejorará al añadir la nueva inversión.

El criterio aplicado al portafolio de indices.

A continuación, formamos un portafolio con una distribución de pesos uniforme entre los activos (Nikkei 225, Hang Seng, y KOSPI).

Code
port_unif <- Return.portfolio(R = retornos, # aqui incluimos la matriz xts de retornos ya calculada
                              weights = c(1/3, 1/3, 1/3), # ponemos los pesos (portafolio uniforme)
                              rebalance_on = NA, # será un portafolio buy and hold (sin rebalanceos periodicos)
                              value = 1)

Calculamos las correlaciones entre los retornos del portafolio y los activos individuales, así como el Sharpe Ratio del portafolio. El Sharpe Ratio es una medida de rendimiento ajustada por el riesgo.

Code
table.Correlation(retornos, # pones la matriz de retornos que deseas hacer analisis de correlación
                  port_unif # pongo los retornos del port uniforme, ya que se requieren
                  )
                                Correlation      p-value  Lower CI  Upper CI
rNikkei225 to portfolio.returns   0.8015993 4.174500e-57 0.7522457 0.8420079
rHangSeng to portfolio.returns    0.6383016 6.777517e-30 0.5582860 0.7065521
rKOSPI to portfolio.returns       0.7912832 1.088701e-54 0.7397341 0.8335989
Code
SharpeRatio(port_unif, # caculamos el SR de este portafolio uniforme
            Rf = 0, # no incluimos en este ejercicio un activo libre de riesgo (aunque sería posible)
            FUN = "StdDev" # la función de referencia para medir riesgo es la volatilidad
            )
                              portfolio.returns
StdDev Sharpe (Rf=0%, p=95%):        0.04046621

Agregar Nuevos Activos al Portafolio

Descargamos datos adicionales para otros activos que consideramos agregar al portafolio, como ASHR (índice SSE Composite), CHIK (índice de tecnología de Shenzhen), 000300.SS (CSI 300), TWII (TAIEX) y SET.BK (SET Index).

Aquí está la explicación detallada de cada uno de los activos mencionados:


1. ASHR (Índice SSE Composite)

ASHR es un ETF (fondo cotizado en bolsa) que replica el rendimiento del índice SSE Composite. Este índice mide el rendimiento de todas las acciones (A-shares y B-shares) que cotizan en la Bolsa de Shanghái, una de las principales bolsas de valores de China. Es un indicador clave del mercado bursátil chino, especialmente para acciones de empresas estatales y grandes corporaciones en sectores tradicionales como finanzas, energía y manufactura.


2. CHIK (Índice de Tecnología de Shenzhen)

CHIK es un ETF que sigue el rendimiento de empresas tecnológicas y de innovación listadas en la Bolsa de Shenzhen. Shenzhen es conocida por ser el centro tecnológico de China, similar a Silicon Valley en EE.UU. Este índice refleja el rendimiento de empresas en sectores como tecnología de la información, inteligencia artificial y biotecnología. Es útil para obtener exposición al sector tecnológico chino y al segmento de empresas en rápido crecimiento.


3. 000300.SS (CSI 300)

El índice CSI 300 es uno de los indicadores bursátiles más importantes de China. Representa el rendimiento de las 300 empresas más grandes y líquidas que cotizan en las bolsas de Shanghái y Shenzhen.
Incluye tanto acciones A de Shanghái como de Shenzhen, abarcando una amplia gama de sectores, desde tecnología hasta bienes raíces y servicios financieros. Es un barómetro clave para medir el rendimiento general del mercado de acciones en China continental.


4. TWII (TAIEX - Índice de Taiwán)

El TAIEX (Taiwan Stock Exchange Weighted Index) es el principal índice bursátil de Taiwán, que mide el rendimiento ponderado por capitalización de las empresas que cotizan en la Bolsa de Valores de Taiwán. Se enfoca en empresas de alta tecnología, especialmente las del sector de semiconductores, en el cual Taiwán es líder mundial. También incluye sectores como manufactura y bienes de consumo.
Este índice es crucial para analizar el mercado taiwanés y su influencia global, especialmente en industrias de alta tecnología.


5. SET.BK (SET Index - Tailandia)

El SET Index es el índice principal de la Bolsa de Valores de Tailandia. Representa el rendimiento promedio de todas las acciones que cotizan en este mercado.
Abarca sectores diversos como banca, turismo, bienes raíces y agricultura, reflejando la estructura económica diversificada de Tailandia. Es un indicador clave para evaluar el mercado bursátil tailandés y el desempeño económico de este país del sudeste asiático.

Esta selección ofrece un enfoque diversificado para evaluar oportunidades en Asia.

Code
getSymbols("ASHR", src = "yahoo", from="2024-01-01", periodicity = "daily")
[1] "ASHR"
Code
getSymbols("CHIK", src = "yahoo", from="2024-01-01", periodicity = "daily")
[1] "CHIK"
Code
getSymbols("000300.SS", src = "yahoo", from="2024-01-01", periodicity = "daily")
[1] "000300.SS"
Code
getSymbols("^TWII", src = "yahoo", from="2024-01-01", periodicity = "daily")
[1] "TWII"
Code
getSymbols("^SET.BK", src = "yahoo", from="2024-01-01", periodicity = "daily")
[1] "SET.BK"
Code
# Calcular los retornos del activo ASHR
rASHR <- Return.calculate(ASHR$ASHR.Close, method = "log")
rASHR <- rASHR[-1]

Igualar Series Temporales

Creamos una función para alinear dos series de tiempo en caso de que tengan diferente longitud. Esto es útil para comparar el portafolio con el nuevo activo.

Code
# Función generalizada para alinear series
igualar_series_n <- function(..., fechas_list) {
  series <- list(...)  # Lista de series de datos
  series_xts <- mapply(function(serie, fechas) {
    xts(serie, order.by = fechas)
  }, series, fechas_list, SIMPLIFY = FALSE)
  series_comb <- do.call(merge, c(series_xts, all = FALSE))
  return(series_comb)
}

La función igualar_series_n permite alinear múltiples series de tiempo utilizando fechas comunes. Su objetivo es recibir varias series con sus respectivas fechas y devolver un único objeto xts en el que las series estén sincronizadas temporalmente. El proceso se puede explicar asi:

  • La función comienza aceptando un número indefinido de series de tiempo como argumentos a través de ... y una lista adicional llamada fechas_list que contiene las fechas correspondientes a cada serie. Lo primero que hace es crear una lista llamada series con todas las series que fueron pasadas como argumento.

  • Luego, se utiliza la función mapply, que permite aplicar una operación a cada par de elementos de dos listas. En este caso, se convierte cada serie junto con su conjunto de fechas en un objeto xts utilizando la función xts(serie, order.by = fechas).

  • La opción SIMPLIFY = FALSE asegura que el resultado sea una lista de objetos xts en lugar de una matriz.

  • Una vez que todas las series han sido transformadas en objetos xts, se fusionan en un único objeto utilizando do.call junto con la función merge. Esta operación toma todas las series y las combina usando sus fechas como referencia, con el argumento all = FALSE para conservar únicamente las fechas que son comunes a todas las series.

  • Como resultado, las fechas en las que al menos una serie no tenga datos se eliminan automáticamente.

  • Por último, la función devuelve el resultado de la fusión: un objeto xts que contiene todas las series alineadas en columnas separadas, con las fechas comunes como índice. Este resultado facilita el análisis conjunto de múltiples series temporales, asegurando que las comparaciones entre ellas sean consistentes en el tiempo.

Si, por ejemplo, tenemos tres series con fechas desiguales, la función selecciona únicamente las fechas en las que todas coinciden, elimina las demás y devuelve un objeto ordenado. Esto es especialmente útil en análisis financieros donde las series de precios o retornos pueden tener huecos temporales.

Uso del criterio SR

Utilizamos el Criterio SR (Sharpe Ratio) para decidir si agregar o no un nuevo activo al portafolio. Este criterio compara el Sharpe Ratio del portafolio actual con el de un portafolio que incluye el nuevo activo.

Code
Criterio_SR <- function(rport, rnuevo, rf){
  SR_port <- SharpeRatio(rport, Rf = rf, FUN = "StdDev") # calculamos el SR del portafolio actual
  SR_nuevo <- SharpeRatio(rnuevo, Rf = rf, FUN = "StdDev") # calculamos el SR del activo nuevo
  rho <- cor(rnuevo, rport)  # calculamos la matriz de correlación
  if (SR_nuevo > rho * SR_port)
    print("Agregue el activo nuevo al portafolio P")
  else 
    print("Es mejor no agregar este activo") # verificamos que se cumple la condición dada
}

Entonces aplicaremos este criterio a cada uno de los activos anteriores. Podemos emplear el siguiente código. Incluyamos para efectos del metodo general un activo libre de riesgo que tiene una tasa \(r_f=0.04\) efectivo anual.

Entonces procedemos a hacer la descarga de la información de los precios de otros indices regionales de referencia de China, Taiwan y Tailandia, los cuales se habían mencionado anteriormente

Code
# Lista de tickers
tickers <- c("ASHR", "CHIK", "000300.SS", "^TWII", "^SET.BK")

# Crear lista de precios ajustados
precios <- list(
  ASHR = Cl(ASHR),
  CHIK = Cl(CHIK),
  `000300.SS` = Cl(`000300.SS`),
  TWII = Cl(TWII),
  SET.BK = Cl(`SET.BK`)
)

# Calcular retornos logarítmicos usando Return.calculate
retornos <- lapply(precios, function(x) Return.calculate(x, method = "log"))

# Extraer fechas de las series
fechas_list <- lapply(retornos, index) 

# Llamar a la función igualar_series_n para alinear todas las series
series_iguales <- do.call(igualar_series_n, c(retornos, list(fechas_list = fechas_list)))
series_iguales <- na.omit(series_iguales)
# Ver las series alineadas
print(head(series_iguales,4))
             ASHR.Close   CHIK.Close X000300.SS.Close    TWII.Close
2024-01-04 -0.015076679 -0.016894808     -0.009293263 -0.0005502958
2024-01-05  0.000000000 -0.014169547     -0.005374342 -0.0017399957
2024-01-08 -0.010909992 -0.018086276     -0.013015737  0.0030502608
2024-01-09 -0.007487376 -0.006794396      0.001957858 -0.0021174534
           SET.BK.Close
2024-01-04  0.003470399
2024-01-05 -0.004632245
2024-01-08 -0.006682133
2024-01-09 -0.002484594

Finalmente aplicamos el criterio a cada uno de los activos sugeridos como “nuevos”:

Code
# Lista de tickers
tickers <- c("ASHR", "CHIK", "000300.SS", "TWII", "SET.BK")

# Crear lista de retornos logarítmicos
retornos <- lapply(tickers, function(ticker) {
  precios <- Cl(get(ticker))  # Obtener precios ajustados
  Return.calculate(precios, method = "log")  # Calcular retornos logarítmicos
})

# Igualar series por fechas comunes
series_iguales <- na.omit(do.call(merge, retornos))  # Combina y elimina NA

# Función simple para evaluar activos
Criterio_SR <- function(rport, rnuevo, rf) {
  SR_port <- SharpeRatio(rport, Rf = rf, FUN = "StdDev")
  SR_nuevo <- SharpeRatio(rnuevo, Rf = rf, FUN = "StdDev")
  rho <- cor(rnuevo, rport)
  if (SR_nuevo > rho * SR_port)
    return("Agregar")
  else 
    return("No agregar")
}

# Evaluar activos
rport <- series_iguales[, 1]  # Usar la primera columna como portafolio base
rf <- 0.04  # Tasa libre de riesgo

# Aplicar la evaluación a cada activo
resultados <- sapply(2:ncol(series_iguales), function(i) {
  rnuevo <- series_iguales[, i]
  Criterio_SR(rport, rnuevo, rf)
})

# Mostrar resultados
names(resultados) <- colnames(series_iguales)[-1]  # Etiquetar con nombres de activos
print(resultados)
      CHIK.Close X000300.SS.Close       TWII.Close     SET.BK.Close 
    "No agregar"     "No agregar"     "No agregar"     "No agregar" 

Desde esta perspectiva, no es propicio agregar ninguno de los indices aca listados, al menos no con la evidencia muestral presentada.