Backtesting con Índices

“42” es la respuesta a la pregunta sobre “la vida, el universo y, ya sabes, todo eso”. Al menos así resulta en el magistral libro Hitchhiker’s Guide to the Galaxy de Douglas Adams, una broma inteligente para indicarnos que si no entendemos la respuesta, tal vez se debe a que tampoco entendíamos del todo la pregunta cuando la formulamos.

Algo parecido sucede con el backtesting financiero, ante la pregunta “¿es esta una buena estrategia de inversión”? Nuestra respuesta será inevitablemente un corolario de “42” a menos que entendamos que valorar es comparar. ¿Buena… Comparada con qué?

Recientemente un conocido me expuso su metodología de inversión, una elaborada mezcla de estimaciones macro con indicadores técnicos construidos alrededor de la teoría de onda de Elliott en su versión Prechteriana. Todo muy complejo e interesante, hasta llegar a la pregunta: “¿y cómo evalúas tu estrategia?”, ante la cual respondió con una mirada un tanto incómoda: “yo, mientras no pierda dinero, ya estoy contento”.

Vaya. Y eso viene de una persona con buena formación, libros publicados y una cierta autoridad en el tema. Sin duda versiones de ese lema campan a sus anchas por las mentes de muchos inversores en los mercados financieros.

Yo, mientras me no pierda dinero, ya estoy contento.

Todos tenemos una aversión natural a las pérdidas, y es sin duda su prerrogativa elegir el objetivo que más felicidad mental le reporte a largo plazo. También es mi obligación como abogado del diablo recordarle que ganar 5% cuando una estrategia con un perfil de riesgo similar gana 10% es, efectivamente perder dinero. Y que, por contra, perder sólo 5% mientras el resto del mercado pierde un 10% puede ser una estrategia muy efectiva para no quedarnos fuera de un rebote de mercado (mi querido socio Carlos Doblado estará encantado de explicarle que lo realmente difícil no es vender, si no volver a comprar, mientras le enseña algunas de sus cicatrices de combate).

En resumen, eso es lo que intenta reflejar el concepto de coste de oportunidad como patrón oro de las evaluaciones: que tal vez hay mejores usos alternativos de su dinero, y es contra tales usos que debe medir las estrategias para estimar su valor. Aquí, como en tantos otros aspectos de las finanzas, dejarnos llevar por nuestros instintos no nos convierte en mejores inversores.

Comparando

¿Y cómo encontramos ese patrón oro? Si recuerdan, en la primera entrega de esta serie comparamos nuestra estrategia de ejemplo en Amadeus con comprar y mantener la inversión durante todo el periodo. Esto nos proporciona una buena base general: comparar una actividad con, simplemente, no hacer nada. Así compararemos primero la gestión activa (aplicar una metodología) con la gestión pasiva (comprar y mantener). Y a continuación, por supuesto podemos aumentar nuestro marco de referencia comparando diferentes metodología de gestión activa.

Ahora bien, normalmente nuestro universo de inversión no incluye una sola acción, como es el caso de nuestro ejemplo, sino muchas, cientos, potencialmente miles de ellas, que pueden formar o no parte de nuestra cartera en diferentes momentos. ¿Cuál es entonces la gestión pasiva? En tales casos conviene usar como referencia un índice del mercado en el cual operamos. Por ejemplo, si sólo invierte en acciones españolas puede usar el popular IBEX35. Si su rango de actividad as mayor, entonces tal vez le interese más usar el EuroStoxx50, un índice compuesto por los 50 títulos más activos y líquidos de Europa. Y siempre puede echar una mirada al S&P500, que es uno de los índices más seguidos del mundo, considerado por muchos la mejor representación del estado de la economía norteamericana y, por ende, mundial. En definitiva, busque índices cercanos a su geografía y actividad que hagan la comparación relevante: como inversor doméstico de poco sirve saber que hubiese ganado más en los mercados emergentes, de los que nada conoce y que no encajan en su perfil de riesgo, pero preocúpese si el IBEX35 obtiene mejores resultados.

Para el próximo ejemplo vamos a bajarnos los datos históricos del S&P 500, tanto en su versión normal (ticker de Yahoo ^GSPC, disponibles desde 1950) como total return (ticker de Yahoo ^SP500TR, disponibles desde 1988). Si el concepto total return le resulta nuevo valga por el momento decir que es el precio que aglutina todas las ganancias generadas, incluyendo por ejemplo los dividendos que las compañías pagan cada cierto tiempo a sus accionista, y por tanto suele ser un mejor reflejo de la rentabilidad de un título.

Obteniendo Índices

Aquí topamos con una primera dificultad, porque si bien Yahoo Finance nos permite consultar las cotizaciones actuales e históricas de múltiples indices a través de su página web, veremos que sólo obtendremos un mensaje de error si intentamos descargarlos a través de PyAlgoTrade.

Esto se debe a un cambio de política en Agosto del 2011 cuando Yahoo se vió obligado a restringir el acceso, forzado por Dow Jones y otras entidades que encuentran sustento comercializando este tipo de información, llegando al acuerdo de que la información puede consultarse online, pero no a través de su API (interfaz de programación). Ahora bien, no todo está perdido, porque si el dato puede ser visualizado en pantalla, también puede ser recopilado. No por un copista humano, por supuesto, que tal vez tardaría años en la improductiva tarea de recrear un sólo índice, sino por un web scrapper, como técnicamente se los conoce en el mundillo: programas diseñados exclusivamente para visitar páginas web y compilar la información en un formato que nos permita su análisis.

Afortunadamente tampoco tenemos que crear nuestro propio web scrapper puesto que Rimon Barr de Cornell University ya ha creado uno para nosotros bajo licencia de código libre llamado pyq, que podemos encontrar aquí. También puedes encontrar pyq en el repositorio Github de este proyecto, PyAlgoSamples. Descargue y descomprima el archivo dentro de su carpeta de backtesting de manera que quede dentro de su propia carpeta, donde debería tener los siguientes archivos:

C:\backtesting\pyq\__init__.py
C:\backtesting\pyq\pyq.py

Ahora para hacer una prueba, vamos a descargarnos el índice Dow Jones Industrial del 2015. Abra una nueva línea de comandos, vaya a la carpeta pyq y escriba esta instrucción:

C:\backtesting\pyq> python pyq.py 20150101 20151231 "^DJI" > "DJI-2015.csv"

En unos breves instantes, no muchos más de los que hubiese requerido el uso de la API, tendremos un nuevo archivo DJI-2015.csv que contiene el histórico diario para ese año. Así, mientras exista en el web, podemos bajarnos cualquier índice, en cualquier rango temporal. ¿Prácticamente mágico, verdad?

Sin duda sorprendente, pero aún tenemos un par de problemas por sortear antes de avanzar con nuestro ejemplo sobre el S&P 500:

  • Los datos no se descargan en el mismo formato que PyAlgoTrade usa; falta la cabecera y sobran alguna columnas.
  • Si bien podemos descargar todos los datos del 1950 al 2015 en un solo archivo, la política de “un archivo por año y activo” es más recomendable para facilitar el mantenimiento y manipulación de nuestro almacén de datos, en cuyo caso requiere mucho esfuerzo de descarga por nuestra parte.

Ambos escollos se pueden solventar escribiendo un poco de código. Coloque este archivo SpxData.py de modo que quede dentro de la raíz de su carpeta de backtesting:

C:\backtesting\SpxData.py

Ejecútelo como ya ha aprendido, o bien desde IDLE o bien desde la línea de comandos, y en breve obtendrá una carpeta C:\backtesting\SpxData (para mejor organización) que contiene todos los archivos para cada año de cada índice. Ahora si que puede exclamar: ¡Magia! … Y utilizar SpxData.py como plantilla para sus futuras descargas de índices desde Yahoo Finance.

Cruce de Oro, Cruce de la Muerte

Ahora que ya contamos con los datos precisos, vamos a poner a prueba una vieja metodología de inversión técnica: empezando con $1.000, comprar cuando la media móvil de 50 sesiones cruza al alza la de 200 sesiones (lo que se conoce como el “Cruce de Oro”) y cerrar nuestra posición cuando cruza a la baja (conocido como “Cruce de la Muerte”). Todo ello comparado con simplemente comprar y mantener el índice. ¿Funcionará?

Para este trabajo podemos reutilizar la mayor parte del código que escribimos en nuestro ejemplo previo, solamente creando dos variables en lugar de una para guardar las medias móviles y adaptando el evento onBars a la lógica de nuestra estrategia. Pero también ha de tener en cuenta dos eventualidades que pueden provocar errores en la ejecución:

Exception: high < close on 2010-07-26 00:00:00

En ocasiones los datos obtenidos de Yahoo no son perfectos y se producen inconsistencias como ésta, en la que el cierre es más bajo que el valor más alto de la sesión. Para indicarle a PyAlgoTrade que corrija estas eventualidades debe incluir esta línea en los feeds:

feed.sanitizeBars(True)

Por otro lado, también puede encontrarse con esto:

Not enough volume to fill ^SP500TR market order [1] for 3 share/s

Si abre cualquiera de los archivos del índice SP500TR podrá ver la causa. ¡El volumen de mercado es siempre 0! El módulo de brokerage de PyAlgoTrade cuenta con una inteligente característica mediante la cual simula las situaciones de reales de falta de liquidez, poniendo a nuestra disposición s0lo una fracción del volumen diario. Pero al carecer de información de volumen, no nos permite comprar nunca. Lo mejor es que desactive esta función por completo incluyendo esta línea en la inicialización de su estrategia:

self.getBroker().getFillStrategy().setVolumeLimit(None)

Es decir, sin límites de volumen. Finalmente, otra mejora que podemos introducir es determinar el tamaño de nuestra posición (el número de acciones a comprar) no basado en un número fijo sino en un porcentaje de nuestro presupuesto, dependiendo de la cotización actual, por ejemplo dejando sólo un 5% de liquidez en nuestra cartera.

amount = int(0.95 * self.getBroker().getEquity() / bar.getAdjClose())

Puede encontrar todos estos cambios en los archivos SpxStrategy.py y SpxBenchmark.py. Para ejecutar ^GSPC o ^SP500TR sólo tiene que cambiar dicho texto en la llamada a run_strategy así como el año de inicio para los cálculos, obteniendo resultados como los siguientes:

figure_1

Rápidamente veremos que ganamos $76.834,36 y $8.444,66 en total return (si bien este incluye más ganancias empieza mucho más tarde, en el 1988, y el efecto composición del índice normal es masivo), pero que con nuestro benchmark ganaríamos $116.544,13 y $11.688,49, respectivamente. Perdemos en ambos casos.

Refinando la Comparación

Algo que podríamos sospechar es que se producen ciertos cruces que duran sólo un día o dos antes de revertir por debajo de la media, y que la actividad de compra y venta volátil que generan reduce la eficiencia del sistema. Esta es una buena hipótesis en la que trabajar disfrutando la flexibilidad de nuestro entorno de backtesting. Aunque le animo a que pruebe realizar sus primeros pinitos de bricolaje financiero con su propia versión, aquí puede descargar una posible implementación en SpxStrategySafe.py, programada para esperar 5 sesiones antes de efectuar operaciones. Obviamente SpxBenchmark.py no requiere cambios.

Al ejecutar el nuevo código, y sin entrar en demasiados detalles, veremos que si antes se ejecutaban 67 operaciones de mercado, ahora se ejecutan 65. Nos ahorramos 2 operaciones, lo cual no es una gran diferencia, habida cuenta que el cambio provoca que nuestra rentabilidad descienda a $69.026,42. Puede jugar con diferente número de corte cambiando el valor de la variable __trigger, pero verá que todos los escenarios son peores que el original. Hipótesis refutada.

Debido volumen de datos que disponemos (65 años de precios diarios), otra interesante línea de trabajo consistiría en realizar la comparación en diferentes rangos temporales, por ejemplo por décadas, con el objetivo de ver si el patrón se mantiene a través del tiempo o por contra diferentes mercados tienes comportamientos divergentes y si podemos discernir alguna correlación  (por ejemplo, si mercados más recientes se comportan de manera diferente que mercados más lejanos en el tiempo, ya que han cambiado mucho mediante la aplicación de tecnología, entre otros).

Con este objetivo, vamos a modificar nuestro código para que se ejecute en serie por décadas, guardando los resultados en el disco en lugar de tenerlos únicamente en pantalla para facilitar la comparación. Para las información textual, si no nos importa que todos los resultados vayan al mismo archivo, esto implica:

import pyalgotrade.logger as logger
logger.file_log = "resultados.txt"

Si no, deberemos pasar un FileHandler a cada Strategy (y recordarnos de eliminarlo después, porque todas las Strategy comparten un mismo Logger).

import logging
# Attach a file handler
fileHandler = logging.FileHandler("resultados.txt")
myStrategy.getLogger().addHandler(fileHandler)
# Detach the file handler
myStrategy.getLogger().removeHandler(fileHandler)

En cuanto a la representación gráfica, podemos mantener plt.plot() pero hemos de encargarnos nosotros de guardarlo en disco:

plt = plotter.StrategyPlotter(myStrategy)
myStrategy.run()
fig = plt.buildFigure(None, None)
fig.savefig("resultados.png")

También cambiaremos nuestro presupuesto inicial de $1.000 a $10.000, ya que si no la apreciación del índice nos impedirá entrar en mercados recientes, y acabaremos por organizarlo todo de una manera limpia en la carpeta SpxResults. Aquí tienen para descarga los archivos SpxStrategyBatch.py y SpxBenchmarkBatch.py, les dejo que obtengan sus propios resultados total return. En cuanto al índice normal, poniendo los resultados en una tabla de Excel se obtiene esta comparativa:

 

Screen Shot 2016-03-21 at 12.37.06 AM

Lo cual refleja negativamente en los Cruces del Oro y de la Muerte, con dos excepciones: la década 1970-1980 así como el 2000-2010, debido al cataclismo del 2008 al que aboca ciegamente el benchmark de buy and hold. Algo es algo.

Próximos Pasos

Pronto aprenderemos a realizar estas comparativas de Excel automáticamente y otros trucos que nos facilitarán nuestro bricolaje financiero. Pero antes de eso, ¿queda así zanjado el tema de las comparativas? Todavía no. De momento solamente hemos estado comparando rentabilidades, y un buen benchmarking tiene que incluir tanto la rentabilidad como el nivel de riesgo una determinada estrategia. No sólo basta con ganar más: ese diferencial nos debe compensar por el riesgo adicional que asumimos.

Lo veremos en la siguiente entrega. Hasta entonces no duden en ponerse en contacto conmigo si tropiezan con algún escollo, intentaré ayudar en lo que pueda y, por favor, no olviden compartir en sus redes sociales. ¡Hasta pronto!

Anuncios

2 comentarios en “Backtesting con Índices

    1. Hola Rafael, acabo de comprobarlo en mi entorno de desarrollo y te confirmo que SpxData.py funciona bien. Estás usando python 2.x? Si usas python 3.x probablemente te suceda esto porque las librerías son incompatibles. Lo puedes comprobar escribiendo “python -V” en la línea de comandos. Si vas a trabajar con varios entornos (2.x, 3.x, diferentes módulos) te recomiendo que instales un gestor de entornos gratuito como Anaconda (https://www.anaconda.com/what-is-anaconda). Un saludo!

      Me gusta

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