Showtime

Podemos oír los gritos de las fans desde el camerino… las voces de miles de lindas adolescentes al borde de la histeria colectiva se entrelazan coreando al unísono: “backtesting!”, “backtesting!”, “BACKTESTING!”…

Ese suele ser el momento del sueño en el que me despierto.

¡Y con suerte, porque aún no estamos listos para el showtime! Antes de poder pasar al siguiente nivel con nuestro entorno de backtesting necesitamos un par de cosas más: unos cuantos datos sobre los que trabajar, además de algunas mejoras técnicas importantes.

De Vuelta a Iberia

Como fuente para llevar a cabo los ejemplos de esta entrada así como otras futuras vamos a utilizar el IBEX35, el índice español por antonomasia que ya presentamos cuando introdujimos el tema de las estrategias multiactivo. Siguiendo esos mismos principios he creado una base de datos que pueden descargar en este enlace: ibex35.sql.zip.

Una vez lo descompriman obtendrán un archivo ibex35.sql de unos 50 MB frente a los 10 originales (de ahí el ZIP ya que las instrucciones de SQL contienen texto muy redundante) que puede cargar dentro de MySQL bien a través de MySQLWorkbench o bien la propia línea de comandos de mysql:

mysql> SOURCE C:/Descargas/ibex35.sql;

Tras un rato de espera y una lluvia de líneas en su monitor, obtendrá una base de datos llamada IBEX35 que contiene todos los precios de los componentes del índice entre Abril del 2001 y Diciembre del 2016, además de otros extras de los que hablaremos más adelante. Para comprobar que funciona puede escribir una instrucción como la siguiente:

mysql> USE ibex35;
mysql> SELECT valor FROM dato WHERE activo = 'TEF.MC' AND fecha='2013-02-08' AND criterio = 'CLOSE';

Si todo ha ido bien verá en pantalla el decimal ‘9.9608900000’, que es el precio de cierre para Telefónica el día 3 de Febrero del 2013 con una precisión de 10 dígitos. ¡Genial!

rock concert

Tiempo para la Memoria

Los problemas de memoria no son una exclusiva de los seres humanos; a pesar de los enormes progresos tecnológicos de los años recientes es una experiencia común que nuestros ordenadores se queden sin memoria cuando les pedimos que procesen una enorme cantidad de datos en un breve espacio de tiempo.

Como aquí hemos estado utilizando cantidades relativamente pequeñas de datos dudo que haya experimentado el problema a menos que haya estado trabajando con un Pentium de cuando la Halliwell todavía estaba con las Spice Girls (disculpen, todos tenemos nuestras debilidades). Pero ahora nos estamos metiendo en las ligas de los mayores. El almacén que acabamos de descargar contiene cerca de un millón y medio de datos. Todavía no es para asustarse, pero sí para prevenirse.

El problema principal consiste en nuestro modus operandi hasta el momento: tanto cuando usábamos CSV como con la primera versión de la base de datos, al solicitar cualquier precio de un instrumento cargábamos automáticamente toda su serie histórica de precios, de principio a fin. PyAlgoTrade opera así por dos razones. La primera es consistencia: al cargar toda la información a la vez, el software tiene ocasión de revisar que los datos están en la secuencia correcta (por eso realmente no importa si nuestros ficheros CSV están ordenados por fecha en orden ascendente o descendente). La segunda es eficiencia: si voy a terminar usando todos los datos para realizar mi backtesting, es más rápido pedirlos todos juntos que uno a uno.

Estos presupuestos dejan de ser tan claros cuando simplemente manejamos demasiada información (billones de datos) o bien cuando, en aras de la flexibilidad para realizar diferentes pruebas que abogábamos en nuestra entrada anterior, nuestro almacén contiene datos fuera del rango de este backtest específico: otros años, otros componentes, otros criterios. Por suerte PyAlgoTrade puede adaptarse sin problemas a nuestras nuevas necesidades.

Por un lado está el alimentador que usamos ahora: DbMemFeed, guarda todo en memoria porque para simplificar le hicimos heredar de la clase barfeed.membf.BarFeed. Pero si en lugar de ello utilizamos un pariente superior tal que la clase barfeed.BaseBarFeed podemos manejar la carga de datos como más nos plazca, lo cual haremos en nuestra una nueva clase DbFeed, y en lugar de cargar todas las barras juntas con getBars tomaremos sólo la del día con getBar (usando un función getNextDate para saber cuál es el siguiente día de mercado en la serie). Cuenta con el código completo en este dbfeed.py mejorado.

class DbMemFeed(barfeed.membf.BarFeed):
class DbFeed(barfeed.BaseBarFeed):

De hecho la clase BaseBarFeed es tan flexible que podemos utilizarla también como base para crear rutinas que procesan datos en tiempo real, por ejemplo conectados a la API de nuestro broker para realizar day trading algorítmico en vivo y en directo. Le recomiendo que explore la clase LiveFeed del módulo xignite.barfeed si quiere investigar en esta dirección que, de momento, posponemos para un futuro. ¡Vaya con cuidado, eso sí que es SHOWTIME en mayúsculas! Ahí el error se paga caro.

Barras Personalizadas

Por otro lado están las propias barras que contienen los datos, y que hemos de personalizar para nuestro uso. Si examina la tabla dato de nuestra base verá que se usa la siguiente nomenclatura para los precios de cada sesión:

  • OPEN: precio de apertura.
  • HIGH: precio más alto de la jornada (cenit).
  • LOW: precio más bajo de la jornada (nadir).
  • CLOSE: precio de cierre:
  • VOL: volumen negociado en la jornada, en millones.
  • ADJ: precio de cierre “ajustado”. Es importante resaltar que todos los precios están ajustados por ampliaciones y spin-offs. Por su parte, el campo “ADJ” está además ajustado por dividendos corporativos. Es decir: total return. Si esto le suena a chino le recomiendo que lea esta entrada anterior.

¿No necesitamos por tanto guardar también un precio total return de apertura, cenit y nadir? Oigo a mi querido socio Carlos exclamar: ¡SIEMPRE! A su sabiduría me remito, que ha escrito largo y tendido en su blog sobre la importancia los ajustes. Siempre querremos tener accesibles datos ajustados de todos esos valores, pero no necesitamos guardarlos sino que los podemos derivar del ratio ADJ / CLOSE, tal como hace PyAlgoTrade por defecto si examinan el objeto BasicBar dentro de pyalgotrade/bar.py. Por ejemplo para el precio de cierre ajustado:

return self.__adjClose * self.__low / float(self.__close)

Cierto, en ADJ podríamos guardar el ratio directamente, pero se suele guardar el cierre ajustado por comodidad ya que el “precio total return del día” es el valor al que se accede de forma más frecuente haciendo trading en tendencia.

También encontrará en el almacén del IBEX35 otros campos que serán pronto de importancia y para los cuales la clase BasicBar que usamos no puede dar cabida. Sin embargo esa clase contiene rutinas bastante útiles para mantener la integridad de la información, comprobando por ejemplo que LOW <= (OPEN y CLOSE) <= HIGH lo cual nunca está de más en este mundo imperfecto, así que en lugar de prescindir de ella la vamos a expander con una matriz para contener dinámicamente tantos campos adicionales como necesitemos. Su código se encuentra dentro del mismo dbfeed.py que descargó arriba.

Nótese que si desea prescindir por completo de los precios, o usar solamente uno (e.g. ADJ) para su backtesting, le resultará más eficiente dejar de lado BasicBar y expandir directamente desde su ancestro, la clase Bar.

Pruebas de Sonido

Ya estamos listos para realizar una prueba más compleja. Si todo está en orden, cuando descargue y ejecute esta versión convenientemente mejorada de DbStrategy.py obtendrá un listado similar al siguiente:

Screen Shot 2016-07-24 at 9.25.51 PM

Aún no hemos aplicado ninguna estrategia, pero ya casi estamos listos para ello. Ante nosotros se encuentran, a modo de ejemplo, tres columnas con los precios de apertura y cierre diarios para BBVA, Repsol y Telefónica desde Enero del 2010 hasta Enero del 2012.

Tres columnas, dijo usted. ¿Qué dato es el tercero entonces? Bien, si examinan el código encontrarán la respuesta dentro de nuestras nuevas barras personalizadas:

bar.getField('PER')

Se trata del PER, es decir, el Price-Earnings Ratio diario de cada instrumento. Momento perfecto para pasar a la publicidad y dejarles, con suerte, en ascuas. ¡Pronto más!

Anuncios

2 comentarios en “Showtime

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