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:
- 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í.
- 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.
- 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.
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.