sábado, 30 de marzo de 2013

Plan de Trabajo

En esta ocasión me gustaría compartir con vosotros el plan de trabajo que me mantendrá ocupado durante las siguientes semanas (y de cuyos avances iré dando cuenta en este blog). Básicamente lo que pretendo crear es un sistema para el desarrollo automático de sistemas de trading, así como para su puesta en marcha, seguimiento y evolución. Evidentemente, el desarrollo estará basado en R.

Las principales elementos en los que se basa el diseño del sistema son:
  • Aunque el sistema global pueda ser particularmente complejo, las estrategias de trading que finalmente se pongan en operativa real deben ser muy simples.
  • A la hora de buscar las estrategias de trading óptimas lo que prima es la robustez de las mismas, y la maximización de los beneficios se deberá alcanzar con la gestión monetaria.
La solución a desarrollar se compone de tres módulos:
  • Módulo de Búsqueda de Estrategias: tiene por objetivo realizar una búsqueda inteligente sobre un amplio espacio de posibles estrategias, seleccionando un conjunto de estrategias candidatas.
  • Módulo de Selección de Estrategias: partiendo de las estrategias candidatas generadas en el módulo anterior selecciona aquellas que son robustas.
  • Módulo de Operativa Real: partiendo de las estrategias robustas, a las que se le añade un sistema de gestión monetaria, realiza trading real. También es responsable del seguimiento y evolución de las estrategias.

Búsqueda de Estrategias
El módulo de Búsqueda de Estrategias a su vez se compone de los siguientes submódulos:
  • Algoritmo de Búsqueda: es el responsable de realizar una búsqueda inteligente por el espacio de soluciones. Se utilizará un algoritmo basado en enjambres de partículas (particle swarm) dado que lo que nos interesa es encontrar un conjunto de óptimos locales, y no el óptimo global.
  • Generador de Candidatos: para la generación de candidatos de estrategias de trading se utilizará una evolución gramatical (gramatical evolution), basada en un conjunto de gramáticas BNF específicamente diseñadas para el caso. Se utilizan gramáticas por su versatilidad a la hora de definir la forma que queremos que adopten las estrategias de trading (tendencial, pares, cointegración, ...).
  • Evaluación Rápida: como función objetivo durante la búsqueda se utilizará un procedimiento rápido de evaluación de estrategias, basado en una versión simplificada del método de Robert Pardo (sin análisis walk-forward). Se utiliza el método de Robert Pardo por su orientación hacia conseguir sistemas robustos.
En el caso de ser necesario se utilizarán técnicas de paralelización basadas en GPUs.

Selección de Estrategias
El módulo de Selección de Estrategias parte del conjunto de estrategias candidatas al que se ha llegado en el apartado anterior, y sobre ellas aplica el método Robert Pardo en su totalidad para filtrar aquellas estrategias que son robustas y presentan una esperanza matemática positiva. Al igual que en caso anterior, en caso de ser necesario se utilizarán técnicas de paralelización basadas en GPUs.

Operativa Real
El módulo de Operativa Real toma las estrategias seleccionadas en el módulo anterior y las pone a operar en una cuenta real de manera automática. Este módulo a su vez se compone de los siguientes submódulos:
  • Gestión Monetaria: se encarga de añadir la parte de gestión monetaria a las estrategias de trading. La estrategia a utilizar se basará en los trabajos de Ralph Vince.
  • Trading: módulo encargado de enviar las señales al mercado. Como broker se utilzará Interactive Brokers ya que dispone de un interfaz para operativa automática con R.
  • Re-optimización: se encarga de la re-optimización periódica de las estrategias, según la periodicidad óptima resultante del análisis walk-forward.
  • Evaluación: se encarga de la evaluación de los resultados obtenidos en real con la operativa de trading, y realimenta al submódulo de gestión monetaria. 

jueves, 14 de marzo de 2013

Integración de MetaTrader y R


Ya hemos hablado en entradas anteriores de las limitaciones que plataformas como MetaTrader presentan a la hora de hacer un análisis cuantitativo de las estrategias de trading, y de cómo con R se podrían cubrir esas limitaciones. En esta ocasión vamos a ver cómo se podría integrar MetaTrader y R para que cada uno haga aquella función que mejor sabe hacer, es decir, con MetaTrader desarrollamos una estrategia y la probamos con datos históricos, y con R analizamos los resultados, optimizamos la estrategia, y realizamos un análisis walk-forward.

Existen varias alternativas para integrar MetaTrader y R. A continuación revisamos brevemente algunas:

MT4 API nos permite acceder a la mayoría de las funciones de MetaTrader (tales como abrir o cerrar órdenes, calcular indicadores técnicos, recuperar el estado de una cuenta, etc) desde un programa en C externo, que a su vez, podría ser llamado desde R. Esta solución es la que mejor nos permitiría integrar MetaTrader con R. Desde R controlaríamos casi todo el proceso, desde definición de la estrategia, testing, optimización, análisis, trading real, etc., y MetaTrader lo utilizaríamos básicamente para abrir y cerrar órdenes (control de stop loss y demás), y llevar el control de la cuenta. El mayor inconveniente es que se trata de una solución cerrada y de pago ($450).

Con R for MetaTrader le daríamos la vuelta al calcetín, es decir, desde MetaTrader podríamos invocar funciones escritas en R. En este caso, el control lo llevaría MetaTrader, lo cual tiene sus limitaciones, ya que el análisis y la optimización de estrategias seguiría siendo la de MetaTrader, y carecemos de walk forward. Esta solución es interesante si queremos desarrollar estrategias de trading avanzadas, por ejemplo, podríamos desarrollar una estrategia basada en Self Organized Maps utilizando R, que se invocaría desde MetaTrader. Además, este producto es libre y gratuito.

La última solución sería procesar los ficheros HTML de resultados generados por MetaTrader e importar los datos en R. Esta es la solución más sencilla, ya que no requiere software adicional, aunque requeriría de algo de programación para hacer una automatización completa del proceso de análisis de una estrategia. Dada su sencillez, en el resto de esta entrada de blog vamos a ver un ejemplo de cómo podemos importar los resultados de MetaTrader en R. Nótese que cuando hablo de resultados me refiero a todo tipo de resultados que se puedan grabar como ficheros tipo HTML, desde el testing de una estrategia, los resultados de una optimización, o los resultados de la operativa real.

Lo primero que tendríamos que hacer es importar (e instalar si no lo hemos hecho ya) el paquete XML de R. Este paquete contiene multitud de utilidades para crear y trabajar con ficheros XML, y sus derivados, como HTML.

library('XML')

A continuación vamos a importar los datos correspondientes a un informe de testing (Strategy Tester Report) generado por MetaTrader sobre una estrategia concreta. Lo que vamos a importar es el listado de entradas que se han producido en el mercado, y la información relativa a las mismas (tipo, número de orden, volumen, precio, S/L, T/P, beneficios y balance). Para ello escribimos la orden:

mitabla = readHTMLTable("C:/Users/Rafael/src/R/CDMM.htm", header=T, colClasses=c("integer", "character", "character", "integer", "numeric", "numeric", "numeric", "numeric", "numeric"), which=2)

El primer argumento es el nombre del fichero HTML generado por metatrader. En el segundo le indicamos que la tabla que contiene los datos dispone de una cabecera, que a su vez será utilizada por R. El tercer parámetro es una lista de tipos de datos, que ayudará a R a interpretar correctamente los valores que va a leer. Y por último, con el parámetro “which” le indicamos a R que lo que queremos importar es la segunda tabla del fichero (la primera corresponde a la tabla resumen).

En este momento tenemos en R una variable llamada “mitabla” a la que le podemos aplicar toda la potencia de análisis de R. Por ejemplo podríamos empezar por

summary(table$Beneficios)

y ya tendríamos más información sobre nuestra estrategia que la que nos proporciona MetaTrader.

martes, 12 de marzo de 2013

Trabajar con Datos de Forex con R y SQLite (5/5)


En esta ultima entrega de esta serie vamos a ver cómo podemos acceder a los datos almacenados en nuestras bases de datos SQLite desde R. Para ello vamos a utilizar el paquete DBI de R. DBI es un paquete que permite acceder de manera unificada a diferentes bases de datos, entre ellas Oracle, MySQL, y SQLite. Cada base de datos cuenta con su propia librería que debe ser previamente cargada en R:

library(“DBI”)
library(“RSQLite”)

A continuación abrimos una conexión con la base de datos. Para ello primero tenemos que crear un driver que nos gestione la conexión, y después abrir la conexión propiamente dicha con el fichero donde se encuentre la base de datos:

drv <- dbDriver(“SQLite”)
db <- dbConnect(drv, “eurusd.db”)

En este momento podemos enviar nuestras consultas SQL a la base de datos, como por ejemplo:

rs <- dbSendQuery(db, “SELECT MAX(high) AS High, MIN(low) as Low, date as Date
    FROM eurusd
    WHERE date > '2010-01-01' AND date < '2011-01-01'
    GROUP BY round(unix / (8 * 60))
    “)

Para recuperar los datos tenemos que utilizar la función fetch(), a la que le debemos indicar cuantas barras queremos recuperar, o -1 para recuperar la totalidad:

EURUSD <- fetch(rs, n=25)

En este momento la variable EURUSD contiene los datos listos para ser analizados.

Cuando no necesitemos recuperar más datos de la consulta, debemos liberar esta con:

dbClearResult(rs)

Y finalmente, para cerrar la conexión con la base de datos hacemos:

dbDisconnect(db)

Y con esto damos por cerrada esta serie de entradas sobre cómo trabajar con datos de Forex desde R y SQLite.

sábado, 9 de marzo de 2013

Gestión Monetaria: El Juego de las Caras


Generalmente, en los cursos de trading nos explican que tener éxito invirtiendo no sólo depende de que utilicemos una estrategia con esperanza positiva, sino que igual de importantes son la gestión monetaria y el control de las emociones, las famosas tres Ms (Mind, Method, Money) de las que habla Alexander Elder en su libro Vivir del Trading. Sin embargo, y por mucho que nos lo repitan, al final acabamos poniendo todos nuestros esfuerzos en encontrar la mejor estrategia, y nos olvidamos de las otras dos componentes del éxito.

Para poner de relieve la importancia de la gestión monetaria y del control de las emociones, me gustaría hablar en esta ocasión de un juego que se celebra todos los Viernes Santo en la pequeña localidad de Calzada de Calatrava (Ciudad Real). El llamado “Juego de las Caras”. Las Caras es un juego de azar en el que los ciudadanos se juegan dinero de verdad (y mucho). La gente que quiere participar en el juego se coloca alrededor de un corro, y en el centro del mismo se coloca una persona que actúa como “banca”. Cada persona alrededor del corro apuesta una determinada cantidad de dinero que la banca debe cubrir. A continuación se lanzan dos monedas. Si salen dos cruces ganan los apostantes; si salen dos caras gana la banca; y si sale cara y cruz, se repite el lanzamiento. Como se puede observar la probabilidad de ganar en este juego es del 50%, y no existe ninguna estrategia que pueda mejorar este porcentaje. Y sin embargo, a la larga gana la banca. ¿Cómo es posible esto?


El primer problema es que la gente suele gestionar mal el dinero. Si dispongo de 50 euros, los apuesto en su totalidad, y salen caras, pierdo todo el dinero y el juego se acabó para mi. Pero aunque sepa gestionar mejor el dinero y apueste de 10 en 10 euros da igual, a poco que tenga una racha bajista (no tienen por que ser las 5 perdidas consecutivas necesariamente, sino un drawdown de 5) lo he perdido todo. Es decir, el que tiene más dinero, tiene más posibilidades de ganar. Y la banca suele tener mucho más dinero que cualquier jugador individual (en la práctica juntan su dinero varias personas para actuar de banca).

El segundo es que la gente no controla sus emociones: ambición y miedo. Son muchos los que después de tres ganancias consecutivas se creen que están en “racha” y quintuplican su apuesta de golpe, perdiendo todo lo que había ganado, y más. Y más difícil de controlar es la contraria, el miedo. Después de tres pérdidas consecutivas la gente decide darse un “descanso” para comprobar con desesperación cómo en las tres siguientes tandas salen cruces. Nótese que la banca no tiene este problema, porque está obligada a cubrir todas las apuestas, luego no puede verse influida por la ambición y el miedo.

Y finalmente está el tema de la diversificación. Nosotros como jugadores individuales no tenemos posibilidad de diversificar nuestras apuestas. Sin embargo la banca está jugando contra jugadores que tiene poco dinero y jugadores que tienen algo más, jugadores que saben gestionar sus apuestas y jugadores que no, y jugadores que a veces se dejan guiar por la ambición y el miedo y a veces no. La banca sí que está diversificando sus apuestas de manera efectiva.

Mi recomendación, por si alguien quiere ser banca este Viernes Santo, es estimar el número medio de jugadores de un corro y la apuesta media por jugador, multiplicar ambos valores y el resultado volverlo a multiplicar por 150. Y si disponéis de ese capital, ¡negocio seguro!

Nos vemos en Las Caras.

martes, 5 de marzo de 2013

Trabajar con Datos de Forex con R y SQLite (4/5)


En la entrada anterior de este blog vimos como crear barras de cualquier longitud utilizando SQLite. Sin embargo, nos encontramos con el problema de que el tiempo de respuesta de las consultas era demasiado elevado. Esto nos puede crear ciertos problemas en operativa real para estrategias que trabajen en barras muy cortas (por ejemplo de un minuto), y sobre todo, supone un gran problema cuando queremos realizar análisis de tipo walk forward, ya que este tipo de análisis requiere de múltiples consultas.

Pero aquí es donde entra en juego las optimizaciones que permite SQLite, y sobre todo, ese misterioso atributo unix que hemos añadido a nuestra tabla.

Utilizando el Tiempo Unix

Para poder mejorar el rendimiento utilizando el atributo unix, lo primero que tenemos que hacer es crear un índice sobre esta columna:

CREATE INDEX unix_index ON eurusd (unix);

Y a continuación veamos cómo podemos consultar el máximo y el mínimo en barras de 8 minutos utilizando este nuevo campo:

SELECT MAX(high) AS High, MIN(low) as Low, date as Date
FROM eurusd
WHERE date > '2010-01-01' AND date < '2011-01-01'
GROUP BY round(unix / (8 * 60));

Y de igual manera se podrían consultar los precios de apertura y de cierre.

Consulta Final

En este momento ya tenemos todo lo que necesitamos para crear barras de cualquier longitud, para cualquier periodo de tiempo, y de una manera eficiente. La consulta final para barras de 8 minutos (8*60=480) sería:

SELECT STRFTIME('%Y-%m-%d %H %M', MIN(date)) AS Date,
    (SELECT open FROM eurusd e2
         WHERE e2.unix >= e1.unix / 480 * 480
         ORDER BY e2.unix ASC LIMIT 1) AS Open,
    MAX(high) as High,
    MIN(low) as Low,
    (SELECT close FROM eurusd e3
         WHERE e3.unix < (e1.unix / 480 + 1) * 480
         ORDER BY e3.unix DESC LIMIT 1) AS Close
FROM eurusd e1
WHERE date > '2010-01-01' AND date < '2011-01-01'
GROUP BY e1.unix / 480;

Otras optimizaciones

Estas consultas se pueden optimizar aun más, pero para ello habría que entrar a programar directamente en el API C de SQLite, un API que nos permite extender la funcionalidad del lenguaje SQL. SQLite dispone de las funciones agregadas MAX y MIN que nos permiten calcular el máximo y el mínimo alcanzado en una barra, pero no dispone de nada equivalente para calcular los precios de apertura y cierre de dicha barra. Es por ello que tenemos que realizar complejas sub-queries que nos retrasan mucho todo el proceso. Este problema se podría solucionar creando nuestras propias funciones agregadas OPEN y CLOSE utilizando el mencionado API de SQLite. Pero esto es un tema demasiado complejo, y que a menos que los lectores demuestren interés, no lo cubriré en este blog.

viernes, 1 de marzo de 2013

Trabajar con Datos de Forex con R y SQLite (3/5)


En la entrada anterior de esta serie vimos como hacer algunas consultas sobre nuestros datos de Forex. En esta entrada lo que vamos a ver es cómo hacer otras consultas más avanzadas, y más útiles.

Si por ejemplo queremos ver el máximo y el mínimo de barras de 8 minutos (sí, sí, de 8 minutos), podemos hacer:

SELECT MAX(high) AS High, MIN(low) as Low, date as Date
    FROM eurusd
    WHERE date > '2010-01-01' AND date < '2011-01-01'
    GROUP BY round(STRFTIME('%s', date) / (8 * 60));

Es decir, podemos agrupar los datos en barras de cualquier longitud, y no sólo las longitudes estándar de 5 minutos, 15 minutos, 1 hora, etc. que ofrecen la mayoría de las plataformas de trading.

Uniendo el ejemplo anterior con las consultas de apertura, máximo, mínimo y cierre de la entrada anterior de este blog, lo que podemos conseguir es agrupar nuestros datos sobre barras de cualquier longitud. Esto nos permite hacer trading en barras de longitudes no convencionales, lo que nos puede suponer una importante ventaja sobre otros traders. Además, también podemos identificar mejor el tamaño de barra óptimo para nuestras estrategias de trading, optimizándolas para cada una las longitudes de barra, desde 1 minuto, hasta por ejemplo 1440 minutos (1 día).

Por ejemplo, para ver los datos agrupados en barras de 13 minutos utilizamos:

SELECT STRFTIME('%Y-%m-%d %H %M', MIN(date)) as Date,
    MAX(CASE WHEN STRFTIME('%s', date) % (13 * 60) = 0 THEN open ELSE 0 END) AS Open,
    MAX(high) as High,
    MIN(low) as Low,
    MAX(CASE WHEN STRFTIME('%s', date) % (13 * 60) = 4 * 60 THEN close ELSE 0 END) AS Close
FROM eurusd
WHERE date > '2010-01-01' AND date < '2011-01-01'
GROUP BY STRFTIME('%s', date) / (13 * 60);

Desgraciadamente esta consulta sólo funciona si nuestros datos no tienen huecos, lo cual no siempre se cumple. Para conseguir el mismo resultado pero con datos que potencialmente pueden tener huecos, tendríamos que utilizar otra consulta algo más compleja:

SELECT STRFTIME('%Y-%m-%d %H %M', MIN(date)) AS Date,
    (SELECT open from eurusd e2
         where CAST(STRFTIME('%s', e2.date) AS INTEGER) >=
         CAST(STRFTIME('%s', e1.date) AS INTEGER) / (13 * 60) * 13 * 60
         order by e2.date asc limit 1) AS Open,
    MAX(high) as High,
    MIN(low) as Low,
    (SELECT close from eurusd e3
        where CAST(STRFTIME('%s', e3.date) AS INTEGER) <
        (CAST(STRFTIME('%s', e1.date) AS INTEGER) / (13 * 60) + 1) * 13 * 60
        order by e3.date desc limit 1) AS Close
    FROM eurusd e1
    WHERE date > '2010-01-01' AND date < '2011-01-01'
    GROUP BY CAST(STRFTIME('%s', e1.date) / (13 * 60) AS INTEGER);

Sin embargo, el lector habrá notado que el tiempo de respuesta de esta última consulta es demasiado elevado. Esto puede crear ciertos problemas en operativa real para estrategias que trabajen en barras muy cortas (de por ejemplo un minuto), y sobre todo, cuando queramos realizar análisis de tipo walk forward, que requieren de múltiples consultas.

Pero aquí es donde entra en juego las optimizaciones que permite SQLite, y sobre todo, ese misterioso atributo unix de nuestra tabla. Pero este es tema para otra entrada de blog.