2 votos

Aproximación de los ratios de Sharpe y Sortino a partir de medias móviles exponenciales

Así que he estado estudiando el papel "Learning To Trade via Direct Reinforcement" Moody y Saffell (2001) que describe en detalle cómo utilizar las estimaciones móviles exponenciales (EMA) de los rendimientos en el momento t ( r_t ) para aproximar los ratios de Sharpe y Sortino de una cartera o valor.

Nota: en el documento se refiere al ratio de Sortino como "Downside Deviation Ratio" o DDR. Estoy bastante seguro de que, desde el punto de vista matemático, no hay ninguna diferencia entre el DDR y el ratio de Sortino.

Así, el documento define dos valores utilizados para aproximarse a cualquiera de los dos ratios, el Ratio de Sharpe Diferencial ( dsr ) y el Ratio de Desviación Diferencial a la Baja ( d3r ). Se trata de cálculos que representan la influencia de la rentabilidad comercial en el momento t ( r_t ) en los ratios de Sharpe y Sortino. Las EMA utilizadas para calcular el DSR y el D3R se basan en una expansión en torno a un índice de adaptación, .

A continuación, presenta una ecuación según la cual debería poder utilizar el DSR o el D3R en el momento t para calcular recursivamente una aproximación móvil de los ratios Sharpe o Sortino actuales en el momento t sin tener que realizar un cálculo sobre todos los t para obtener el resultado exacto. Esto es muy conveniente en un entorno con un horizonte temporal infinito. Desde el punto de vista computacional, los datos acabarían siendo demasiado grandes para recalcular la relación completa de Sharpe o Sortino en cada paso de tiempo t si hay millones de pasos de tiempo.

$$S_t |_{\eta>0} \approx S_t|_{\eta=0} + \eta\frac{\partial S_t}{\partial \eta}|_{\eta=0} + O(\eta^2) = S_{t-1} + \eta\frac{\partial S_t}{\partial \eta}|_{\eta=0} + O(\eta^2)$$ $$D_t \equiv \frac{\partial S_t}{\partial \eta} = \frac{B_{t-1}\Delta A_t - \frac{1}{2}A_{t-1}\Delta B_t}{(B_{t-1} - A_{t-1}^2)^{3/2}}$$ $$A_t = A_{t-1} + \eta \Delta A_t = A_{t-1} + \eta (R_t - A_{t-1})$$ $$B_t = B_{t-1} + \eta \Delta B_t = B_{t-1} + \eta (R_t^2 - B_{t-1})$$

Esta es la ecuación para utilizar el DSR para calcular el ratio de Sharpe en el tiempo t . En mi opinión, los valores más grandes de podría causar más fluctuación en la aproximación ya que pondría más "peso" en los valores más recientes para r_t pero, en general, los ratios de Sharpe y Sortino deberían seguir dando resultados lógicos. Lo que en cambio encuentro es que ajustando cambia salvajemente la aproximación, dando valores totalmente ilógicos para los Ratios de Sharpe (o Sortino).

Del mismo modo, las siguientes ecuaciones son para el D3R y para aproximar el DDR (también conocido como ratio de Sortino) a partir de él:

$$DDR_t \approx DDR_{t-1} + \eta \frac{\partial DDR_t}{\partial \eta}|_{\eta=0} + O(\eta^2)$$ $$D_t \equiv \frac{\partial DDR_t}{\partial \eta} = \\ \begin{cases} \frac{R_t - \frac{1}{2}A_{t-1}}{DD_{t-1}} & \text{if $R_t > 0$} \\ \frac{DD_{t-1}^2 \cdot (R_t - \frac{1}{2}A_{t-1}) - \frac{1}{2}A_{t-1}R_t^2}{DD_{t-1}^3} & \text{if $R_t \leq 0$} \end{cases}$$ $$A_t = A_{t-1} + \eta (R_t - A_{t-1})$$ $$DD_t^2 = DD_{t-1}^2 + \eta (\min\{R_t, 0\}^2 - DD_{t-1}^2)$$

Me pregunto si estoy interpretando mal estos cálculos. Aquí está mi código Python para ambas aproximaciones de riesgo donde es self.ram_adaption :

def _tiny():
    return np.finfo('float64').eps

def calculate_d3r(rt, last_vt, last_ddt):
    x = (rt - 0.5*last_vt) / (last_ddt + _tiny())
    y = ((last_ddt**2)*(rt - 0.5*last_vt) - 0.5*last_vt*(rt**2)) / (last_ddt**3 + _tiny())
    return (x,y)

def calculate_dsr(rt, last_vt, last_wt):
    delta_vt = rt - last_vt
    delta_wt = rt**2 - last_wt
    return (last_wt * delta_vt - 0.5 * last_vt * delta_wt) / ((last_wt - last_vt**2)**(3/2) + _tiny())

rt = np.log(rt)

dsr = calculate_dsr(rt, self.last_vt, self.last_wt)
d3r_cond1, d3r_cond2 = calculate_d3r(rt, self.last_vt, self.last_ddt)
d3r = d3r_cond1 if (rt > 0) else d3r_cond2

self.last_vt += self.ram_adaption * (rt - self.last_vt)
self.last_wt += self.ram_adaption * (rt**2 - self.last_wt)

self.last_dt2 += self.ram_adaption * (np.minimum(rt, 0)**2 - self.last_dt2)
self.last_ddt = math.sqrt(self.last_dt2)

self.last_sr += self.ram_adaption * dsr
self.last_ddr += self.ram_adaption * d3r

Tenga en cuenta que mi rt tiene un valor que oscila en torno a 1.0 donde los valores >1 beneficios medios y <1 pérdidas medias (mientras que un perfecto 1.0 significa que no hay cambios). Primero hago rt en rendimientos logarítmicos tomando el logaritmo natural. _tiny() es sólo un valor muy pequeño (algo así como 2e-16 ) para evitar la división por cero.

Mis problemas son:

  1. Esperaría que los ratios de Sharpe y Sortino aproximados cayeran en el rango de 0,0 a 3,0 (más o menos) y en su lugar obtengo un ratio de Sortino monótonamente decreciente, y un ratio de Sharpe que puede explotar hasta valores enormes (más de 100) dependiendo de mi tasa de adaptación . El índice de adaptación debería afectar al ruido en la aproximación, pero no hacerla explotar así.
  2. El D3R es (de media) más negativo que positivo, y acaba aproximándose a una relación sortina que cae de forma casi lineal, que si se deja iterar durante el tiempo suficiente puede alcanzar valores totalmente disparatados como -1000.
  3. De vez en cuando hay muy grandes saltos en la aproximación que, en mi opinión, sólo podrían explicarse por algún error en mis cálculos. Los ratios de Sharpe y Sortino aproximados deberían tener una evolución algo ruidosa pero constante, sin saltos masivos como los que se ven en mis gráficos.

Monotonically decreasing Sortino ratio due to on-average negative D3R Huge explosion of Sharpe ratio with relatively high adaption rate =0.1

Por último, si alguien sabe dónde puedo encontrar otras implementaciones de código existentes en las que se utilice el DSR o el D3R para aproximar los ratios de Sharpe/Sortino, lo agradecería mucho. He podido encontrar esta página de AchillesJJ pero no sigue realmente las ecuaciones planteadas por Moody, ya que está recalculando la media completa de todos los pasos de tiempo anteriores para llegar al DSR de cada paso de tiempo t . La idea central es poder evitarlo utilizando las medias móviles exponenciales.

2voto

Evan Fosmark Puntos 17732

Para cualquiera que aún siga esto:

Me di cuenta de que las ecuaciones y mi código funcionan bien; el problema era que tenía que escalar los rendimientos antes de hacer los cálculos de riesgo para evitar la pérdida de datos de precisión float32, y también sólo que mi valor para era demasiado alto. Bajando mi valor a <= 0.0001 produce aproximaciones de sharpe y sortino totalmente lógicas. Como nota al margen, esto también permite que mi red neuronal aprenda directamente de los cálculos marginales de sharpe y sortino, lo cual es genial.

Además, el uso de rendimientos logarítmicos era problemático para la aproximación de sortino, así que lo cambié efectivamente a rt = (rt - 1) * scaling_factor lo que hace que la aproximación de sortino ya no tienda hacia valores negativos.

Los rendimientos logarítmicos habrían funcionado bien si mi único objetivo fuera utilizar el DSR/D3R como cálculo de pérdidas en mi red neuronal, pero para obtener buenas aproximaciones de sortino no funciona ya que enfatiza fuertemente los rendimientos negativos y suaviza los positivos.

1voto

Ant Puntos 121

Si lo que le preocupa es la eficiencia computacional a la hora de calcular Sharpe/Sortino en cantidades grandes y crecientes de datos, puede utilizar métodos incrementales/en línea para calcular las medias, las desviaciones estándar, etc. en todo el conjunto de datos. A continuación, utilice el último valor calculado en línea para el Sharpe/Sortino de todo el conjunto de datos. Esto evitará el problema de que los datos más antiguos tengan menos peso que los más recientes, que está implícito cuando se utilizan las EMA.

Mi respuesta en el SE de Ciencia de Datos en https://datascience.stackexchange.com/questions/77470/how-to-perform-a-running-moving-standardization-for-feature-scaling-of-a-growi/77476#77476 ofrece más detalles y un enlace.

0voto

Rainer Puntos 433

Esto ha sido muy, muy útil, gracias. He aplicado esto a un algoritmo RL (sólo la métrica DSR) y tengo algunas cosas que preguntar si este hilo sigue activo.

  1. Parece que los valores son inestables al principio de la secuencia.

  2. Además, ¿a qué valores iniciarías las medias móviles?

  3. También he experimentado una caída repentina durante el entrenamiento enter image description here

¿Por qué crees que es así?

Este es tu código, sólo cambié el nombre y lo puse en una clase, espero haberlo hecho bien

clase DifferentialSharpeRatio: def init (self, eta=1e-4): self.eta = eta auto.último_A = 0 auto.último_B = 0

def _differential_sharpe_ratio(self, rt, eps=np.finfo('float64').eps):
    delta_A = rt - self.last_A
    delta_B = rt**2 - self.last_B

    top = self.last_B * delta_A - 0.5 * self.last_A * delta_B
    bottom = (self.last_B - self.last_A**2)**(3 / 2) + eps

    return (top / bottom)[0]

def get_reward(self, portfolio):
    net_worths = [nw['net_worth'] for nw in portfolio.performance.values()][-2:]
    rt = pd.Series(net_worths).pct_change().dropna().add(1).apply(np.log).values

    dsr = self._differential_sharpe_ratio(rt)

    self.last_A += self.eta * (rt - self.last_A)
    self.last_B += self.eta * (rt**2 - self.last_B)

    return dsr

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