Ratio Sortino en Python:
Haciendo mi propia investigación, encontré un documento que describe la fórmula del ratio de Sortino que sigue la misma configuración que la de este enlace descrito en un comentario anterior. En el caso de la brevedad, la fórmula del ratio de Sortino puede especificarse como:
$$ S = \frac{R - T}{TDD} $$
donde $R$ es el rendimiento del periodo medio y $T$ es la rentabilidad objetivo (también denominada rentabilidad media aceptable, MAR). Además,
$$ TDD = \sqrt{\frac{1}{N}\sum_{i=1}^N \min\left(0, X_i - T\right)^2} $$
denota la desviación a la baja objetivo, con $X_i$ siendo el $i$ de la rentabilidad. Utilizando la fórmula anterior podemos calcular el ratio de Sortino en Python. Sin tener en cuenta la primera parte de su código anterior ( definir los pesos, obtener los datos de las existencias, etc. ), podemos calcular el ratio de Sortino utilizando la siguiente función:
def SortinoRatio(df, T):
"""Calculates the Sortino ratio from univariate excess returns.
Args:
df ([float]): The dataframe or pandas series of univariate excess returns.
T ([integer]): The targeted return.
"""
#downside deviation:
temp = np.minimum(0, df - T)**2
temp_expectation = np.mean(temp)
downside_dev = np.sqrt(temp_expectation)
#Sortino ratio:
sortino_ratio = np.mean(df - T) / downside_dev
return(sortino_ratio)
Ahora puede construir la rentabilidad de la cartera, definir su objetivo de rentabilidad especificado $T$ y los introducimos en la función:
#portfolio returns:
port_ret = returns.dot(weights)
#-------------------output:-----------------------
print(np.round(SortinoRatio(port_ret, T = 0),4))
0.0782
El resultado es un ratio Sortino de 0,0782.
Verificación:
Una buena forma de validar la función anterior, es encontrar una función ya implementada de una fuente "respetable" . Aquí, el PerformanceAnalytics
paquete de R
contiene una función que calcula el ratio de Sortino. Utilizando esta función en las 10 primeras filas de su marco de datos ( llamado ret ) obtenemos lo siguiente:
#R code:
ret <- matrix(
c(0.001729, 0.000323, 0.003116,
-0.015906, -0.006137, 0.000546,
-0.001849, -0.010400, 0.004001,
0.006648, 0.006897, 0.002882,
-0.008821, -0.012720, 0.001747,
-0.011375, -0.006607, -0.009381,
0.014106, 0.009312, 0.008326,
-0.005792, 0.020099, 0.002426,
-0.016712, -0.003230, -0.010823,
0.044238, 0.007777, 0.012500), nrow = 10, ncol = 3, byrow = TRUE)
weights <- c(1/3, 1/3, 1/3)
portret <- as.xts(ret %*% weights, order.by = as.Date(1:10))
#-------------------output:-----------------------
round(SortinoRatio(portret, MAR = 0), 4)
[,1]
> Sortino Ratio (MAR = 0%) 0.1664
Aquí, la función en Python da el mismo resultado (hay una ligera diferencia para los decimales más altos):
return_test = returns.head(10)@weights
#-------------------output:-----------------------
print(np.round(SortinoRatio(return_test, T = 0),4))
0.1664
Hay una ligera desviación entre los dos resultados para más decimales ( >5 ), lo que podría atribuirse a los diferentes valores de precisión utilizados entre las lenguas. No obstante, espero que esto le ayude en su aplicación .