Backtesting Multiactivo

Si tiene ocasión de visitar la Bolsa de Madrid no se pierda el salón de los Pasos Perdidos. Entre la suntuosa belleza de este palacio de estilo neoclásico inaugurado en 1893 podrá constatar cinco impactos de bala en una de sus puertas, herencia de la Guerra Civil Española; un agrio pero conveniente recuerdo tanto de la historia convulsa de nuestro país como del hecho de que las finanzas no existen en una realidad aparte del entorno político y social.

Le menciono la Bolsa de Madrid porque para nosotros ha llegado el momento de dar un gran paso adelante con nuestro sistema de backtesting. Mientras aprendíamos los elementos básicos he deliberadamente constreñido nuestro punto de mira en sólo un título. Pero en una situación normal trabajaremos con un universo compuesto de muchos, muchos activos. Y en nuestro caso utilizaremos para ello los componentes del IBEX 35.

¿Por qué trabajar múltiples activos? La respuesta es simple: si usted está esperando una oportunidad en un sólo activo, puede pasarse esperando mucho tiempo. Con estrategias multiactivo puede optimizar el uso de su tiempo y su dinero, manteniendo posiciones simultáneamente en varios de ellos según se vayan abriendo o cerrando oportunidades.

¿Y por qué circunscribirnos a los componentes de un índice? Pues porque el índice representa “lo mejor” del ámbito inversor ibérico, empresas robustas y con resultados sólidos. Es cierto que se puede argumentar que en estas empresa una gran parte de la oportunidad ya “se ha evaporado” respecto a otras empresas más pequeñas y con gran potencial. Pero usar tal filtro también nos protege de los “chicharros” que nos pueden explotar en las manos, especialmente ahora que aún no sabemos muy bien discriminar unas oportunidades de otras. Aprendamos a caminar antes de querer correr.

SOCIEDADES-RESULTADOS
Bolsa de Madrid

Datos

Puede obtener tanto la cotización del IBEX 35 como la lista de componentes en Yahoo Finance, bajo el ticker ^IBEX. Ninguno de ellos tiene restricciones así que podremos incluirnos en nuestro entorno de backtesting  a través de PyAlgoTrade. Para simplificar vamos a limitar nuestro estudio a 5 años, del 2010 al 2015, pero como no todos los activos tienen cotizaciones históricas desde el 2010 (por ejemplo, IAG empieza en el 2011) deberemos evolucionar un poco nuestra rutina de descarga. Aquí le dejo IbexAssets.py e IbexData.py con el trabajo completo. IbexAssets no debe ejecutarse directamente pues sólo contiene los activos y otros parámetros por separado, para que no tengamos que escribirlos de nuevo al crear nuestras estrategias. Ejecute en cambio IbexData, que importará los parámetros de IbexAssets y llevará a cabo la descarga. En breve tendrá todos los datos necesarios en la carpeta C:\backtesting\IbexData con la comodidad y eficiencia a la que estamos acostumbrados.

Nota Mayo 2017! Yahoo ha dejado de proporcionar esta API (el anuncio está aquí). La opción es descargar los archivos manualmente con la función de descarga del web, o bien utilizar esta solución a través de pandas_datareader. Para ello tenéis que importar dos módulos nuevos:

C:\backtesting> pip install pandas_datareader
C:\backtesting> pip install fix_yahoo_finance

Contáis con un ejemplo completo en este archivo BasicYahooFix del proyecto en GitHub.

Estrategia

De nuevo para simplificar nuestra metodología va a ser la que ya conocemos: el Cruce de Oro y Cruce de la Muerte ejemplificados en las media móviles de 50 y 200 sesiones. Aplicaremos ese sistema sobre los 35 constituyentes del IBEX, y lo compararemos con un simple “Buy and Hold” del índice.

Esto nos va a requerir evolucionar considerablemente nuestro código. En primer lugar hemos de ser capaces de guardar las medias móviles de 35 activos, así como posiciones simultáneas en varios de ellos, con lo que _positions, _smaShort y _smaLong van a tener que convertirse en listas (diccionarios, técnicamente) que podamos consultar por valor:

self._positions = {}
self._positions[instrument] = position

También hemos de tener en cuenta que no todos los instrumentos van a estar disponibles desde el principio (e.g. IAG) y que no todos los instrumentos van a tener una posición en un momento determinado, por lo cual hemos de comprobar su existencia antes de operar:

if instrument in bars:
    if instrument not in self._positions:

Por otro lado la estrategia tarda 200 sesiones en ponerse en marcha (el tiempo mínimo que requiere la creación del SMA largo) mientras que en una situación real ya existiría desde el primer día, por tanto vamos a introducir un elemento de delay en el benchmark para que espere también 200 sesiones y así hacer una comparación más justa.

Les dejo en IbexSma.py el código con las modificaciones necesarias para trabajar con estrategias multiactivo. Verá que he unificado estrategiabenchmark en un sólo archivo. Ya que comparten la mayoría de su funcionalidad (excepto por la implementación de onBars) es útil que tengan una relación de parentesco entre sus clases: MyStrategy (compleja) hereda de MyBenchmark (simple), que a su vez hereda de la clase base de backtesting en PyAlgoTrade, strategy.BacktestingStrategy. Para cambiar entre  estrategia y benchmark sólo tiene que modificar los parámetros de la función run_strategy en el código.

Métricas

Ahora que hemos cubierto todo el espectro disponible de StrategyAnalyzers, también he aprovechado para limpiar, organizar y ampliar las métricas que usamos, convirtiendo los valores de decimales (0.98) a porcentajes (90%) e incluyendo valores anualizados tanto para el ratio de Sharpe como la volatilidad y la rentabilidad.

Tener valores por año y no necesariamente por todo el periodo de inversión nos será muy útil en un futuro cuando queramos comparar  la rentabilidad de estrategias con duraciones diferentes. En cuanto a la rentabilidad anualizada, tenga en cuenta que no puede aplicar una simple división debido al efecto composición del que ya hablamos en su momento. La fórmula que debemos aplicar es:

cagr-formula1

Para ello primero guardamos el valor inicial de nuestra inversión:

capStart = myStrategy.getBroker().getEquity()

Después, cuando empezamos la ejecución de la estrategia, registramos la fecha inicial:

self.startDateTime = feed.peekDateTime()
self.endDateTime = None

A continuación, registramos la fecha final usando el evento onFinish:

def onFinish(self, bars):
    self.endDateTime = bars.getDateTime()

Y finalmente guardamos el valor final tal y como hicimos en un principio:

capEnd = myStrategy.getBroker().getEquity()

Ahora ya podemos aplicar la fórmula para obtener la rentabilidad anualizada:

rentAn = 100 * (math.pow((capEnd / capStart),(365.0 / ((myStrategy.endDateTime - myStrategy.startDateTime).days))) - 1)

En último lugar, hemos de tener en cuenta que un gráfico con 35 líneas es extremadamente difícil de leer, así que le indicaremos al sistema que no queremos que incluya los diferentes instrumentos en los charts con un conveniente False.

plt = plotter.StrategyPlotter(myStrategy, False)

Comprar Todo

Ya estamos listos para obtener resultados. Primero vamos a ver el benchmark:

run_strategy(True, assets.indices, 1, smaShort, smaLong)

Los resultados son bastante descorazonadores: el patrón de referencia pierde un 9% de del capital con métricas pésimas.

ibex_benchmark

Vamos a examinar ahora nuestra estrategia:

run_strategy(False, assets.instruments, 35, smaShort, smaLong)

ibex_estrategia

Buenas noticias, con nuestra simple estrategia no sólo lo hacemos mejor, sino que llegamos a ganar dinero (un modesto 1.5% de rentabilidad total) con un riesgo mucho más controlado, que se refleja en la baja volatilidad (4.9% vs 14.7%) y pérdida máxima (12.19% vs 33.89%). Gráficamente, fíjese en lo contenida que está la serie histórica de las volatilidades hasta el tempestuoso 2015… pero hasta en ese caso su cenit está por debajo del nadir de la volatilidad del benchmark. Todo ello resulta en última instancia en un Ratio de Sharpe muy superior (0.2 vs -5.5).

Deberíamos sentirnos satisfechos hasta en términos del cuestionable lema de mi amigo en post previos: si recuerdan, “yo mientras no pierda dinero ya estoy contento”.

Comprar Parte

Pero no nos detengamos aquí, vamos a ver cómo podemos mejorar, y para ello le conmino a que examine el número informativo que aparece con cada orden de mercado en nuestra pantalla:

[INFO] [30] COMPRA MTS.MC 2015-06-11 00:00:00 - Price: 9.985 - Amount: 43 - Fee: 0

Ese [30] representa el número de posiciones que tenemos en un momento dado. Y he elegido ese valor en concreto porque vera que se trata del máximo, y llega tarde, ya en el 2015. Es decir, nunca llegamos a abarcar todos los constituyentes. Esto es un problema porque como reservamos capital para 35 posiciones, hay una gran parte de nuestro capital que permanece en liquidez por no producirse las oportunidades esperadas, limitando nuestro potencial de rentabilidad.

¿Qué sucedería si decidiésemos reservar capital sólo para 20 posiciones? Esto es lo bueno de un entorno de backtesting: no hace falta especular, podemos saber.

run_strategy(False, assets.instruments, 20, smaShort, smaLong)

ibex_estrategia2

Tal vez chocante, pero cierto. Con un perfil de inversión similar, incluso gráficamente, y con métricas comparables en términos de riesgo, obtenemos una rentabilidad del 6.57%, casi 4 veces superior. Nuestro Ratio de Sharpe salta hasta un 12.50 ya que aprovechamos mejor una estrategia que, fundamentalmente, funciona.

Comprar Poco

Estos resultados tan buenos nos pueden empujar a pensar… ¿Qué sucedería si reducimos el ámbito aún más, y reservamos capital sólo para 10 inversiones? Vamos allá:

run_strategy(False, assets.instruments, 10, smaShort, smaLong)

ibex_estrategia3

¿Estás de broma? ¡Obtenemos una rentabilidad del 25.94%! Un Ratio de Sharpe del 35.98, excelente a pesar del mayor riesgo que asumimos. ¡Y todo eso, recuerde, mientras la estrategia de gestión pasiva “buy and hold” pierde un 9%!

Conclusión

¿Demasiado bonito para ser verdad? Lo es. Primero, porque al reducir tanto nuestro número de posiciones, estamos poniendo en compromiso lo que resulta ser el mayor beneficio de una estrategia multiactivo: la diversificación. Al no diversificar  lo suficiente o, en castellano sencillo, al “poner todos los huevos en la misma cesta” estamos muchos más expuestos al efecto suerte, y los resultados pueden ser nefastos.

Ya hemos mencionado la volatilidad en otros artículos y volveremos a verla pronto para un análisis con el detalle que merece, pero de momento hágame caso y planifique su capital para mantener en todo momento una cartera diversificada de unas 20 inversiones, ciertamente nunca menos de 10 y raramente más de 30.

Pero segundo y mucho más importante, porque es falso. Se trata de un resultado irreal, una quimera, un producto del uso erróneo de precios y dividendos, así como del sesgo de los supervivientes. Veremos todo ello y más en nuestra próxima entrega.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s