Cuando empecé a trabajar con los sistemas automáticos de trading, una de las primeras tareas que me autoasigné fue el desarrollar una metodología de trabajo que me permitiese determinar, de forma cuantitativa y dentro de un cierto intervalo de confianza, si un determinado sistema tiene esperanza matemática positiva, además de identificar cual es la estrategia de gestión del riesgo que mejor se le ajusta, y el tamaño óptimo de las posiciones a abrir. Una vez desarrollada la metodología, me llevé la desagradable sorpresa de que no existía en el marcado ninguna herramienta lo suficientementemente potente como para poder poner en marcha mi nueva metodología.
A partí de ahí, y gracias a mi formación como ingeniero informático, me puse a desarrollar mi propia herramienta de análisis de sistemas de trading. Con el tiempo, la herramienta llegó a un nivel de madurez que me hizo plantearme la posibilidad de venderla como un producto comercial. Como emprendedor sabía que sólo tienen éxito aquellos productos que los clientes necesitan, quieren, y están dispuestos a pagar por ellos. Pero ¿cómo no iban a querer los traders una herramienta que dado un sistema te dice si es o no rentabe, y además te ayuda a maxizar el retorno mientras minimiza el riesgo? Pues no, eso no es lo que quieren los traders. Al menos no es lo que quieren los traders individuales, que es el mercado al que me dirigía.
Efectivamente, la gente lo que quiere es una "Máquina Mágica". Es decir, una maquinita que pulses un botón y automáticamente salga dinero, sin que sea necesario dedicarle tiempo ni esfuerzo. Eso es lo que me estaban demandando. Y cuando le explicaba que dicha máquina no existe, los potenciales clientes no lo entendían, o mejor dicho, no lo querían entender. Algunos incluso se enfadaban, y me decían que el problema era que no quería compartir mis secretos (pero oiga, ha visto usted que tenga un Mercedes aparcado en la puerta de mi casa).
Así que no tuve más remedio que cancelar la versión comercial de Entropycs. Desgraciadamente ser empresario es incompatible con ser trader, ya que ambas son profesiones a tiempo completo, y hay que optar por una u otra. En cualquier caso, sigo mejorando la plataforma para mi uso interno, y seguiré dando cuenta de mis avances en este blog.
Aceptamos donativos en Bitcoins: 18MaW8h4Yk2fQAdNoNEHdYTP1LhX7NhHhf
sábado, 17 de agosto de 2013
martes, 30 de julio de 2013
Indicador Técnico: RSI
El Índice de Fuerza Relativa, o RSI (del inglés Relative Strength Index), es un oscilador diseñado para medir la velocidad con la que varían los precios. El indicador RSI oscila entre los valores 0 y 100 y se calcula de la siguiente manera:
Donde AG es la ganancia media (del inglés Average Gain), es decir, la media aritmética de las últimas N barras con resultados positivos (y donde N es el periodo de cálculo del indicador), y AL es la pérdida media (del inglés Average Loss), o la media artimética de las pérdidas de las últimas N barras.
Por ejemplo, un RSI calculado sobre 14 barras podría ser el siguiente:
Tradicionalmente se dice que tenemos un mercado en sobrecompra cuando el RSI es superior a 70, y un mercado en sobreventa cuando es inferior a 30. Por tanto, un sencillo sistema de trading podría ser abrir en corto cuando tenemos un mercado en sobrecompra, y abrir en largo cuando está en sobreventa.
En el paquete TTR de R el indicador RSI se calcularía de la siguiente forma:
RSI(price, n = 14, maType)
donde maType es el tipo de media móvil que queremos utilizar para el cálculo del RSI (simple, exponencial, ...). Por ejemplo, el RSI(14) se calcularía como:
data(ttrc)
price <- ttrc[,"Close"]
rsi <- RSI(price, n=14)
Como resultado la variable macd contendría algo similar a:
100 RSI = 100 - -------- 1 + RS RS = AG / AL
Donde AG es la ganancia media (del inglés Average Gain), es decir, la media aritmética de las últimas N barras con resultados positivos (y donde N es el periodo de cálculo del indicador), y AL es la pérdida media (del inglés Average Loss), o la media artimética de las pérdidas de las últimas N barras.
Por ejemplo, un RSI calculado sobre 14 barras podría ser el siguiente:
Tradicionalmente se dice que tenemos un mercado en sobrecompra cuando el RSI es superior a 70, y un mercado en sobreventa cuando es inferior a 30. Por tanto, un sencillo sistema de trading podría ser abrir en corto cuando tenemos un mercado en sobrecompra, y abrir en largo cuando está en sobreventa.
En el paquete TTR de R el indicador RSI se calcularía de la siguiente forma:
RSI(price, n = 14, maType)
donde maType es el tipo de media móvil que queremos utilizar para el cálculo del RSI (simple, exponencial, ...). Por ejemplo, el RSI(14) se calcularía como:
data(ttrc)
price <- ttrc[,"Close"]
rsi <- RSI(price, n=14)
Como resultado la variable macd contendría algo similar a:
67.21311 68.33130 68.90238 71.65472 74.12134 74.12134 75.12550 70.69429 ...
martes, 23 de julio de 2013
Bolsa, timos y estadística
Cuando estudiaba matemáticas, en la asignatura de probabilidad y estadística de primero solían poner como ejemplo un sencillo timo en bolsa. El engaño era el siguiente: seleccionamos un gran número de inversores en bolsa (por ejemplo 1024), a continuación a la mitad le enviamos el Lunes una carta donde le decimos que durante la semana la bolsa subirá, y a la otra mitad otra carta donde le decimos que la bolsa bajará; a la semana siguiente, a los 512 inversores para los que hemos acertado les enviamos otra carta, nuevamente a la mitad le decimos que la bolsa subirá y a la otra mitad que bajará; a la semana siguiente, volvemos a hacer lo mismo con los 256 inversores para los que ya hemos acertado dos veces en nuestro pronóstico; al cabo de varias semanas nos quedarán unos 16 inversores para los que siempre hemos acertado; llegados a este punto le enviamos una última carta donde nos presentamos, les decimos cualquier parrafada de lo buenos que son nuestros métidos de predicción, y les ponemos como ejemplo las 6 semanas consecutivas que hemos acertado, y les decimos que si quieren la previsión para la siguiente semana nos tienen que pagar mil euros. Evidentemente, es difícil que alguien caiga en el timo, pero si lo organizamos a gran escala, empezando con 65.536 inversores y parando con 1024, a poco que pique uno de cada diez, tenemos un negocio redondo.
Este timo, que parece tan obvio, es utilizado habitualmente, por ejemplo, por los bancos: si preguntamos en un banco por un fondo de inversión rentable, seguro que nos muestran cuatro o cinco que nunca han perdido dinero; el cómo lo hacen es muy simple, se trata de abrir muchos fondos e ir cerrando aquellos que entren en pérdidas, de tal manera que los que queden tienen una rentabilidad continuada. También es un método utilizado por los gurús de la bolsa, en este caso el método consiste en hacer muchas predicciones cada día, y al día siguiente sólo recordad aquellas en las que han acertado.
Pero también se utiliza, y esto no es tan evidente, para el caso de los robots, o expert advisors, de trading automático. Ya que existen miles de robots funcionando, por pura estadística, algunos de ellos debe necesariamente ser rentables. Los vendedores nos muestran la rentabilidad pasada de dichos robots, y nos repiten una y otra vez que no se trata de "backtesting" o de rentabilidades en cuentas demo, sino de rentabilidades en real, como garantía de la eficacia del robot. Pero al igual que pasaba con nuestro timador de las cartas, lo normal es que estos robots, tarde o temprano, acaben abandonando la lista de robots con rentabilidades sostenidas en el tiempo, momento en el cual serán sustituidos por otros.
El que un robot haya tendio una rentabilidad sostenible en el pasado no quiere decir absolutamente nada, y por tanto, no debemos jugarnos nuestro dinero con ellos. Si queréis comprar un robot, seleccionar hoy un grupo de robots prometedores, seguir sus rentabilidades durante un tiempo prudencial, por ejemplo un año (el tiempo requerido depende de la frecuencia de las inversiones), y después decidir la compra.
Este timo, que parece tan obvio, es utilizado habitualmente, por ejemplo, por los bancos: si preguntamos en un banco por un fondo de inversión rentable, seguro que nos muestran cuatro o cinco que nunca han perdido dinero; el cómo lo hacen es muy simple, se trata de abrir muchos fondos e ir cerrando aquellos que entren en pérdidas, de tal manera que los que queden tienen una rentabilidad continuada. También es un método utilizado por los gurús de la bolsa, en este caso el método consiste en hacer muchas predicciones cada día, y al día siguiente sólo recordad aquellas en las que han acertado.
Pero también se utiliza, y esto no es tan evidente, para el caso de los robots, o expert advisors, de trading automático. Ya que existen miles de robots funcionando, por pura estadística, algunos de ellos debe necesariamente ser rentables. Los vendedores nos muestran la rentabilidad pasada de dichos robots, y nos repiten una y otra vez que no se trata de "backtesting" o de rentabilidades en cuentas demo, sino de rentabilidades en real, como garantía de la eficacia del robot. Pero al igual que pasaba con nuestro timador de las cartas, lo normal es que estos robots, tarde o temprano, acaben abandonando la lista de robots con rentabilidades sostenidas en el tiempo, momento en el cual serán sustituidos por otros.
El que un robot haya tendio una rentabilidad sostenible en el pasado no quiere decir absolutamente nada, y por tanto, no debemos jugarnos nuestro dinero con ellos. Si queréis comprar un robot, seleccionar hoy un grupo de robots prometedores, seguir sus rentabilidades durante un tiempo prudencial, por ejemplo un año (el tiempo requerido depende de la frecuencia de las inversiones), y después decidir la compra.
Etiquetas:
bancos,
bolsa,
expert advisors,
fondos de inversión,
guru,
robots,
timo
lunes, 1 de julio de 2013
Indicador Técnico: MACD
El indicador técnico MACD, o Media Móvil Convergente/Divergente (del inglés Moving Average Convergence / Divergence) es un oscilador que se basa en la diferencia de dos medias móviles, una media móvil lenta, y una media móvil rápida. El MACD se desglosa en tres componentes:
El indicador MACD combina lo mejor de dos mundos: los indicadores de tendencia y los osciladores. Sin embargo, dado que el indicador no se encuentra acotado dentro de un rango predefinido, es difícil utilizarlo para detectar niveles de sobrecompra o sobreventa.
Un sistema sencillo basado en el indicador MACD podría ser el siguiente: cuando la Línea MACD cruza al alza a la Línea de Señal abrimos en largo, y cuando la Línea MACD cruza a la baja a la Línea de señal abrimos en corto.
En el paquete TTR de R el indicador MACD se calcularía de la siguiente forma:
MACD(x, nFast = 12, nSlow = 26, nSig = 9, maType)
donde maType es el tipo de media móvil que queremos utilizar para el cálculo del MACD (simple, exponencial, ...). Por ejemplo, el MACD(12, 26, 9) se calcularía como:
data(ttrc)
macd <- MACD( ttrc[,"Close"], 12, 26, 9, maType="EMA" )
Como resultado la variable macd contendría algo similar a:
- Línea MACD: media móvil rápida - media móvil lenta
- Línea de Señal: media móvil aplicada sobre la Línea MACD
- Histograma MACD: Línea MACD - Línea de Señal
El indicador MACD combina lo mejor de dos mundos: los indicadores de tendencia y los osciladores. Sin embargo, dado que el indicador no se encuentra acotado dentro de un rango predefinido, es difícil utilizarlo para detectar niveles de sobrecompra o sobreventa.
Un sistema sencillo basado en el indicador MACD podría ser el siguiente: cuando la Línea MACD cruza al alza a la Línea de Señal abrimos en largo, y cuando la Línea MACD cruza a la baja a la Línea de señal abrimos en corto.
En el paquete TTR de R el indicador MACD se calcularía de la siguiente forma:
MACD(x, nFast = 12, nSlow = 26, nSig = 9, maType)
donde maType es el tipo de media móvil que queremos utilizar para el cálculo del MACD (simple, exponencial, ...). Por ejemplo, el MACD(12, 26, 9) se calcularía como:
data(ttrc)
macd <- MACD( ttrc[,"Close"], 12, 26, 9, maType="EMA" )
Como resultado la variable macd contendría algo similar a:
macd signal [1,] 1.6680643 2.1294258 [2,] 1.6230876 2.0281582 [3,] 1.6606279 1.9546521 [4,] 1.5104438 1.8658105 [5,] 1.3984768 1.7723437
sábado, 15 de junio de 2013
Indicador Técnico: Rango Verdadero Medio
El Rango Verdadero Medio, o ATR (del inglés Average True Range), es un indicador técnico que mide la volatilidad de un símbolo. El ATR se basa en los rangos (diferencia entre los precios máximo y mínimo) de las velas, con algunas modificaciones para tener en cuenta la existencia de huecos. Definimos el rango verdadero como:
rango_verdadero = máx[(máximo-mínimo), abs(máximo-cierre_prev), abs(mínimo-cierre_prev)]
Y a continuación, el rango medio de las útimas N barras es la media simple, o exponencial, de los últimos N rangos verdaderos.
Por ejemplo, un ATR de 14 barras sería el sigiente:
Generalmente, el ATR no se utiliza por sí mismo con un criterio de apertura en un sistema, en lugar de eso, se suele utilizar en combinación con otros indicadores técnicos, o simplemente como un filtro de órdenes. Por ejemplo, podemos crear un sistema basado en el cruce de dos medias móviles, y utilizar el indicador ATR para situar nuestros niveles de stop loss: si el símbolo va más allá de N veces el ATR en la dirección contraria a nuestra orden, podríamos decir que el movimiento no puede explicarse por las fluctuaciones debidas al ruido del mercado, sino que seguramente se trate de un cambio de tendencia, y por tanto, deberíamos cerrar la orden.
En el lenguaje Entropys el ATR se calcula como:
ATR(VECTOR, n)
Donde VECTOR es una serie temporal de precios, como por ejemplo aquella dada por la variable del sistema SYMBOL, y n es la longitud utilizada para calcular el ATR.
Para calcular un ATR de longitud 14 sobre el símbolo actual utilizaríamos algo como:
ind <- ATR(SYMBOL, 14)
Como resultado, la variable ind contendría algo similar a lo siguiente:
tr atr trueHigh trueLow
2011-01-03 03:30:00 0.0000 0.0004642857 1.3283 1.3283
2011-01-03 03:45:00 0.0006 0.0004739796 1.3282 1.3276
2011-01-03 04:00:00 0.0002 0.0004544096 1.3282 1.3280
2011-01-03 04:15:00 0.0009 0.0004862375 1.3291 1.3282
rango_verdadero = máx[(máximo-mínimo), abs(máximo-cierre_prev), abs(mínimo-cierre_prev)]
Y a continuación, el rango medio de las útimas N barras es la media simple, o exponencial, de los últimos N rangos verdaderos.
Por ejemplo, un ATR de 14 barras sería el sigiente:
Generalmente, el ATR no se utiliza por sí mismo con un criterio de apertura en un sistema, en lugar de eso, se suele utilizar en combinación con otros indicadores técnicos, o simplemente como un filtro de órdenes. Por ejemplo, podemos crear un sistema basado en el cruce de dos medias móviles, y utilizar el indicador ATR para situar nuestros niveles de stop loss: si el símbolo va más allá de N veces el ATR en la dirección contraria a nuestra orden, podríamos decir que el movimiento no puede explicarse por las fluctuaciones debidas al ruido del mercado, sino que seguramente se trate de un cambio de tendencia, y por tanto, deberíamos cerrar la orden.
En el lenguaje Entropys el ATR se calcula como:
ATR(VECTOR, n)
Donde VECTOR es una serie temporal de precios, como por ejemplo aquella dada por la variable del sistema SYMBOL, y n es la longitud utilizada para calcular el ATR.
Para calcular un ATR de longitud 14 sobre el símbolo actual utilizaríamos algo como:
ind <- ATR(SYMBOL, 14)
Como resultado, la variable ind contendría algo similar a lo siguiente:
tr atr trueHigh trueLow
2011-01-03 03:30:00 0.0000 0.0004642857 1.3283 1.3283
2011-01-03 03:45:00 0.0006 0.0004739796 1.3282 1.3276
2011-01-03 04:00:00 0.0002 0.0004544096 1.3282 1.3280
2011-01-03 04:15:00 0.0009 0.0004862375 1.3291 1.3282
sábado, 1 de junio de 2013
Programación Genética vs Gramáticas Evolutivas
Una de las principales problemas que se encuentran aquellos que descargan la plataforma Entropycs por primera vez es que el concepto de gramática evolutiva es difícil de entender. Y más concretamente, son muchos los que confunden las gramáticas evolutivas con la programación genética, y no es lo mismo. En esta entrada de blog intentaré explicar las diferencias entre ambas técnicas de optimización y búsqueda.
Quizás nos ayude a entender la diferencia si utilizamos el símil bioquímico de cómo se produce la evolución en la naturaleza. La información que nos permite "construir" un organismo vivo viene almacenada en una cadena de pares de bases denominada ADN, que a su vez se desliga en una cadena de ARN, que es interpretada generando secuencias de aminoácidos, que finalmente se convierten en las proteínas que son necesarias para el desarrollo de la vida. Cuando dos individuos se aparean, su descendencia herada una recombinación del ADN de los padres.
Tanto la programación genética como las gramaticas evolutivas se basan en mismo concepto de recombinación de la información de los padres; pero mientras las gramáticas evolutivas lo que recombinan son las cadenas de ADN (al igual que sucede en la naturaleza), la programación genética lo que recombina son las propias protenias finales. Traladado esto al mundo de la informática y de los lenguajes de programación, tenemos que las gramáticas evolutivas combinan cadenas de ADN que son interpretadas (por las gramáticas) para producir programas, y la programación genética lo que recombina son los propios programas.
En mi opinión, al añadir un nuevo nivel de abstracción introduciendo el concepto de gramática, se gana mucha flexibilidad, y se eliminan algunos de los principales problemas que nos encontramos con la programación genética.
Por ejemplo, las gramáticas evolutivas se basan en algoritmos genéticos para realizar la búsqueda de aquellos programas que mejor se adaptan a nuestras necesidades, pero nada nos impede utilizar otras técnicas de búsqueda, como son los enjambres de partículas, o evolución diferencial.
Por otro lado, con la programación genética el principal problema que nos encontramos es cómo garantizar que los programas que resultan de una recombinación son sintácitcamente correctos. Con las gramáticas evolutivas este problema simplemente desaparece, ya que la derivación de los programas, al estar basada en una gramática, por definición, son sintácticamente correctos.
Espero que con esta entrada haya quedado algo más clara la diferencia entre una optimización mediante gramáticas evolutivas y otra mediante programación genética.
Quizás nos ayude a entender la diferencia si utilizamos el símil bioquímico de cómo se produce la evolución en la naturaleza. La información que nos permite "construir" un organismo vivo viene almacenada en una cadena de pares de bases denominada ADN, que a su vez se desliga en una cadena de ARN, que es interpretada generando secuencias de aminoácidos, que finalmente se convierten en las proteínas que son necesarias para el desarrollo de la vida. Cuando dos individuos se aparean, su descendencia herada una recombinación del ADN de los padres.
Tanto la programación genética como las gramaticas evolutivas se basan en mismo concepto de recombinación de la información de los padres; pero mientras las gramáticas evolutivas lo que recombinan son las cadenas de ADN (al igual que sucede en la naturaleza), la programación genética lo que recombina son las propias protenias finales. Traladado esto al mundo de la informática y de los lenguajes de programación, tenemos que las gramáticas evolutivas combinan cadenas de ADN que son interpretadas (por las gramáticas) para producir programas, y la programación genética lo que recombina son los propios programas.
En mi opinión, al añadir un nuevo nivel de abstracción introduciendo el concepto de gramática, se gana mucha flexibilidad, y se eliminan algunos de los principales problemas que nos encontramos con la programación genética.
Por ejemplo, las gramáticas evolutivas se basan en algoritmos genéticos para realizar la búsqueda de aquellos programas que mejor se adaptan a nuestras necesidades, pero nada nos impede utilizar otras técnicas de búsqueda, como son los enjambres de partículas, o evolución diferencial.
Por otro lado, con la programación genética el principal problema que nos encontramos es cómo garantizar que los programas que resultan de una recombinación son sintácitcamente correctos. Con las gramáticas evolutivas este problema simplemente desaparece, ya que la derivación de los programas, al estar basada en una gramática, por definición, son sintácticamente correctos.
Espero que con esta entrada haya quedado algo más clara la diferencia entre una optimización mediante gramáticas evolutivas y otra mediante programación genética.
Etiquetas:
enjambres de partículas,
evolución diferencial,
gramática evolutivas,
programación genética
sábado, 25 de mayo de 2013
¿Cómo saber si un sistema es rentable? (3)
Seguimos con la tercera entrada de nuestra metodología de desarrollo y análisis de sistemas de trading.
Una vez se tenga claramente definida la estrategia a seguir (como vimos en nuestra entrada anterior), se deberá proceder a implementar la estrategia mediante alguno de los lenguajes de programación existentes en las distintas plataformas de trading. Este tarea se compone de dos pasos:
1.- Desarrollo
Durante la fase de desarrollo sólo se espera que el código compile y que genere señales de entrada y salida en el mercado aproximadamente correctas (pueden contener errores e imprecisiones), y que no den ningún tipo de mensajes de error. El código fuente debe seguir los estándares de calidad que hayan sido marcados (indentación, comentarios, etc), e idealmente debería estar basado en plantillas de desarrollo ya existentes.
Durante la fase de desarrollo sólo se espera que el código compile y que genere señales de entrada y salida en el mercado aproximadamente correctas (pueden contener errores e imprecisiones), y que no den ningún tipo de mensajes de error. El código fuente debe seguir los estándares de calidad que hayan sido marcados (indentación, comentarios, etc), e idealmente debería estar basado en plantillas de desarrollo ya existentes.
2.- Depuración
En este apartado se deberán comprobar las entradas y salidas individuales generadas por el sistema, así como los cálculos intermedios realizados. El objetivo es determinar si la implementación que se ha realizado de la estrategia es correcta. Para ello se deberá realizar una simulación histórica de la misma. El tamaño de la muestra para la simulación debe lo suficientemente grande para que se produzcan varias entradas en el mercado para cada una de las posibles combinaciones de reglas del sistema, filtros aplicables, gestión del riesgo y gestión monetaria. Durante la simulación se deberán utilizar valores “razonables” para los distintos parámetros de configuración, ya que en este paso lo importante es comprobar que las reglas funcionan correctamente, y no tanto evaluar la rentabilidad del sistema. A continuación se deberán analizar, barra a barra, las distintas entradas y salidas del mercado que se han producido, y comprobar que se ajustan a lo esperado.
Ejemplo: Cruce de dos Medias Móviles
Desarrollo
La estrategia ha sido programada mediante el lenguaje mql4 y la plataforma MetaTrader 4. El análisis de los datos ha sido realizado utilizando el lenguaje de programación R. Para los datos históricos, se utilizarán los datos en barras de 1 minuto proporcionados por el broker XTB, y que serán agrupados en barras de distintas longitudes (5m, 15m, 30m, etc).
Si alguien tiene interés en conseguir una copia del código, puede ponerse directamente en contacto conmigo y se la haré llegar por correo electrónico.
Depuración
Se ha comprobado el comportamiento correcto del sistema en los siguientes supuestos:
- Entradas en largo y en corto
- Re-entradas correctas después de una salida debido a un stop loss
Para las pruebas se ha utilizado una media móvil corta de 5 barras (una semana), una media móvil intermedia de 11 barras (medio mes), y una media móvil larga de 22 barras (aproximadamente un mes). El sistema se prueba sobre barras diarias para el símbolo EURUSD, durante los años 2010 y 2011. Nótese que cada vez que se ha encontrado un error, y se ha procedido a su corrección, la totalidad de los casos de prueba han sido repetidos (regression testing).
Los supuestos de prueba han sido articulados según los siguientes casos:
- Entradas en largo
- Entradas en corto
- Re-entrada después de SL
- Entrada incial
A modo de ejemplo, se muestran los resultados de las pruebas correspondientes al caso de entradas en largo:
Caso 1: Entrada en largo
El caso 1 se centra en la comprobación de las entradas en largo.
Objetivo: Comprobar que cuando la media móvil corta cruza al alza a la media larga se abre una nueva posición en largo, con el stop loss adecuado, y se cierra la posición en corto si la hubiera.
Resultado Esperado: Cierre y apertura de una nueva posición.
Resultado Obtenido: En el gráfico se puede observar que el cruce de la media móvil corta al alza sobre la media móvil larga se produce el 17/6/10. En este momento se cierra la posición que teníamos abierta en corto, y se abre una nueva posición en largo, con el correspondiente SL situado en la media móvil intermedia.
El caso 1 se centra en la comprobación de las entradas en largo.
Objetivo: Comprobar que cuando la media móvil corta cruza al alza a la media larga se abre una nueva posición en largo, con el stop loss adecuado, y se cierra la posición en corto si la hubiera.
Resultado Esperado: Cierre y apertura de una nueva posición.
Resultado Obtenido: En el gráfico se puede observar que el cruce de la media móvil corta al alza sobre la media móvil larga se produce el 17/6/10. En este momento se cierra la posición que teníamos abierta en corto, y se abre una nueva posición en largo, con el correspondiente SL situado en la media móvil intermedia.
martes, 21 de mayo de 2013
¿Cómo saber si un sistema es rentable? (2)
-->
Continuamos con esta serie de entradas
en la que explicamos nuestra metodología, basada en los trabajos de Robert Pardo, para decidir a
priori si un sistema es o no rentable.
Formulación
El primer paso en la definición de un
sistema de trading es especificar de forma clara y concisa cual es la
idea de inversión subyacente. Una vez especificada la idea que se
pretende implementar, se deben detallar la lista de indicadores
técnicos que se utilizarán en su implementación, indicando cual es
la función de cada uno dentro de la idea global del sistema. Así
mismo se deberán especificar claramente, y sin ambigüedades, cuales
son las reglas de entrada y salida del marcado, idealmente mediante
pseudocódigo. A continuación se describirán los filtros, si es que
existen, que serán aplicados a dichas reglas de entrada y de salida,
y cual es el efecto esperado de cada uno. Finalmente se deberá
detallar la política de gestión del riesgo (stop loss o similar) y
de gestión monteraria (tamaño de las posiciones, recogida de
beneficios, etc.) que serán utilizados como parte del sistema.
Por tanto, una correcta formulación
del sistema se debe cubrir los siguientes aspectos:
- Entradas y salidas
- Gestión de riesgo
- Gestión monetaria
Ejemplo: Cruce de Dos Medias Móviles
El sistema Cruce de Dos Medias Móviles
(abreviado como CDMM) es un sistema de seguimiento de tendencia
basado en el cruce de dos medias móviles: una media móvil corta y
una media móvil larga. Cuando la media móvil corta cruza al alza a
la media móvil larga, nos ponemos en largo, y cuando vuelve a cruzar
a la baja, cerramos la posición. Igualmente, pero a la inversa,
sucede para el caso de las posiciones en corto.
Indicadores Técnicos
El sistema CDMM se basa en el uso de
dos indicadores técnicos:
- Media móvil de corta duración
- Media móvil de larga duración
El objetivo de la media móvil corta es
seguir estrechamente los movimientos del símbolo, pero
proporcionando una señal atenuada que facilite el análisis de su
comportamiento. La media móvil corta nos evita, por tanto, falsas
señales de entrada y salida en el mercado debido a los movimientos
bruscos y de corta duración que suceden en los mercados.
El objetivo de la media móvil larga es
determinar la tendencia del mercado: bajista o alcista. Para que sea
efectiva debe tener una longitud relativamente larga, para que pueda
coger las tendencias en su totalidad, y no se salga del mercado en
los retrocesos.
Existen multitud de tipos de medias
móviles que pueden ser utilizadas en el sistema CDMM: medias móviles
simples, medias móviles exponenciales, medias móviles con
ponderación lineal, etc. Sin embargo, según la literatura
existente, no está claro que al variar el tipo de media móvil se
pueda obtener una mejora significativa de los resultados del sistema.
Por tanto, y para no aumentar innecesariamente el número de
parámetros a optimizar, el tipo de media móvil a utilizar será
fijado en medias móviles simples.
El cálculo de la media móvil se puede
aplicar a distintos tipos de precios: cierre, alto, bajo, apertura,
media, típico y ponderado. Según qué mercados, aplicar el
indicador sobre un tipo de precio u otro puede variar
significativamente el resultado del sistema. Por ejemplo, en los
mercados regulados de acciones el precio de cierre de la sesión es
un dato muy importante a tener en cuenta (subasta final). Sin
embargo, en el mercado de divisas, al ser un mercado continuo, el
precio de cierre de una barra pierde parte de ese significado
especial. Por tanto, las medias móviles de este sistema serán
calculadas sobre el precio medio de cada barra (la suma de los
precios máximo y mínimo dividida por dos) al entender que este
valor es el que mayor información proporciona sobre el contenido de
dicha barra.
Reglas
Sea M(i) el valor de la media móvil larga en
la barra i, y m(i) el valor de la media móvil corta en
la barra i. El sistema CDMM se basa en las
siguientes reglas:
- Si m(i) >= M(i) y no tenemos ninguna posición abierta, entonces podemos abrir en largo.
- Si m(i) >= M(i) y estamos en corto, entonces podemos cerrar la posición.
- Si m(i) < M(i) y no tenemos ninguna posición abierta, entonces podemos abrir en corto.
- Si m(i)< M(i) y estamos en largo, entonces podemos cerrar la posición.
- El sistema no entrará en el mercado hasta que no se produzca el primer cruce de medias. Igual sucede con las re-entradas después de que una posición haya sido cerrada por un stop loss.
Stop Loss y Take Profit
Siguiendo la máxima de cortar las
pérdidas cuanto antes, y dejar correr los beneficios, se utilizará
un Stop Loss ajustado, pero suficientemente amplio para no salirse de
las posiciones debido a los movimientos aleatorios del mercado. Para
el cálculo del stop loss se utilizará una tercera media móvil de
longitud intermedia, es decir, mayor que la media móvil corta, pero
menor que la media móvil larga. Habitualmente en estos casos se
utiliza un stop loss basado en algún indicador de volatilidad como el
ATR. Pero en el sistema CDMM este tipo de indicadores serían
rendundantes ya que el objetivo de la media móvil corta es
precisamente filtrar la volatilidad que pudiera tener el símbolo.
Nótese que cuando se cierra una posición debido al salto de un stop
loss no se podrá volver a abrir una nueva posición hasta que se
produzca un nuevo cruce de las medias móviles.
No se accederá al recurso de recoger
beneficios con un Take Profit.
Filtros Aplicables
No se utilizarán filtros adicionales
en esta estrategia.
jueves, 16 de mayo de 2013
¿Cómo saber si un sistema es rentable? (1)
-->
El principal problema al que se
enfrenta el desarrollador de sistemas de trading quantitativo es el
de determinar a priori si un sistema de trading es rentable o no lo
es. Para ello, el desarrollador cuenta con la herramienta de las
simulaciones históricas, es decir, comprobar si el sistema hubiera
proporcionado beneficios de haberse utilizado en algún periodo de
tiempo pasado. Sin embargo, aunque el sistema demuestre ser rentable
en periodos de tiempo pasados, siempre nos quedará la duda de si lo
que hemos encontrado es precisamente eso, un sistema capaz de
describir a la perfección el pasado, pero con habilidad nula para
predecir el futuro. Este riesgo es aun mayor si hemos utilizado algún
tipo de optimización en el desarrollo de nuestro sistema, ya que
existe el riesgo de caer en una sobre-optimización del mismo. Y
precisamente, evitar las sobre-optimizaciones, es una de las tareas
más difíciles a las que se enfrentan los desarrolladores de
sistemas.
Al día de hoy, la mejor metodología
para desarrollar un sistema, y determinar a priori si será o no
rentable en la operativa real con cierto grado de confianza, es la
metodología propuesta por Robert Pardo en su libro
“The Evaluation and Optimization of Trading Strategies”. Sin
embargo son muchos más los detractores de la metodología de Pardo
que sus seguidores, y son muchas las críticas que ha recibido. A mi
entender la mayoría de las críticas se deben a una incorrecta
interpretación de las ideas que plantea Pardo. El problema es que
el libro tiene importantes carencias: está sobre-escrito, los
contenidos están algo desorganizados, y se hecha de menos algunos
ejemplos concretos de desarrollo de estrategias que clarifiquen los
conceptos. Esto obliga a que sea necesario releer el libro varias
veces (en mi caso llevo ya 6), antes de comprender bien las ideas de
Pardo.
Para intentar corregir este problema,
en esta serie de entradas voy a describir la metodología de Robert
Pardo (con algunas mejoras de mi propia cosecha), paso a paso, y con
un ejemplo concreto, para que no quede ningún tipo de dudas sobre
qué es lo que hay que hacer exactamente. Evidentemente, nuestra
plataforma Entropycs [http://www.entropycs.com]
(de la que en breve daremos noticias) proporciona todas aquellas herramientas
que son necesarias para poner en práctica esta metodología.
La estrategia que vamos a utilizar como
ejemplo es una estrategia muy simple basada en el cruce de dos medias
móviles. Y como buen punto de partida de nuestro documento de
descripción y análisis de la estrategia, recomiendo empezar
escribiendo una pequeña introducción donde se explique brevemente
cual es la idea subyacente a la estrategia. Allá vamos.
Introducción
El sistema Cruce de Dos Medias Móviles
(abreviado como CDMM) es un sistema de seguimiento de tendencia
basado en el cruce de dos medias móviles: una media móvil corta y
una media móvil larga. Cuando la media móvil corta cruza al alza a
la media móvil larga es una señal de que nos encontramos ante una
tendencia al alza, como muestra la siguiente figura:
Y cuando la media móvil corta cruza a
la baja a la media móvil larga, probablemente nos indique el inicio
de una tendencia a la baja:
Los sistemas basados en el cruce de
medias móviles son ampliamente conocidos entre los desarrolladores,
pero ello no significa necesariamente que no puedan ser rentables
cuando son configurados correctamente.
La principal desventaja de los sistemas
de trading basados en el cruce de medias móviles es que cuando el
mercado está lateral se producen muchas falsas señales de entrada
que causan importantes pérdidas. El sistema CDMM intenta corregir
este problema aplicando la máxima del trading de cortar cuanto antes
las pérdidas y dejar correr los beneficios, con el objetivo de que
los beneficios recogidos en las tendencias compensen ampliamente las
pérdidas producidas durante los periodos de mercado lateral.
Actualmente existen innumerables
extensiones y mejoras que pueden ser aplicadas a los sistemas basados
en el cruce de medias móviles (triples medias, varios tipos de
caracterizaciones, combinación con otros indicadores, etc.), pero
dichas extensiones no serán analizadas en el presente documento, al
entender que la mayoría de ellas aportan poco, o muy poco, al
rendimiento del sistema básico.
miércoles, 24 de abril de 2013
¿Cúantos Sistemas de Trading Existen?
-->
En una entrada anterior de este blog vimos cómo mediante el uso de gramáticas se podían
generar automáticamente sistemas de trading. Evidentemente la
siguiente pregunta está clara, de entre todos estos posibles sistemas, ¿cómo buscamos aquellos que son rentables? Existen
muchas alternativas para la búsqueda, desde el uso de algoritmos
genéticos (de ahí lo de gramáticas evolutivas), hasta las nubes de
partículas, pasando por la amplia colección de técnicas de
optimización que existen (y de las que algún día hablaremos en
este blog). Sin embargo, en esta entrada de blog vamos a plantearnos
otra opción, que aunque en un principio parece una locura, quizás
no lo sea tanto. La pregunta es: ¿podríamos evaluar TODOS los
sistemas de trading existentes?
Imaginemos que queremos trabajar con
sistemas de trading basados en la combinación de indicadores
técnicos. En el paquete TTR de R tenemos una colección de más de
30 indicadores. ¿De cuantas maneras posibles se podrían combinar?
Empecemos el caso más básico, consistente en comparar un indicador
técnico con un número entero; si es superior a dicho entero, nos
ponemos en corto, y si es inferior, nos ponemos en largo:
SI (“indicador” op_bol “entero”)
largo SINO corto
Por ejemplo,
SI (RSI(14) < 10) largo SINO corto
Evidentemente, la mayoría de los
sistemas así desarrollados no tendrían ningún sentido, y darían
pérdidas, pero si queremos buscar sistemas nuevos que no hayan sido
antes explorados, no deberíamos descartar ninguna idea a priori.
¿Cuantos sistemas existen basados en
este formato? Pues si contamos con 33 posibles indicadores técnicos,
con un conjunto de 30 posibles enteros a evaluar para cada indicador
(de 0 a 9, de 10 a 90 con incrementos de 10, y de 100 a 900 con
incrementos de 100), y dos operadores boleanos ('>' y '<')
hacen un total de 59.400 sistemas. Ahora bien, ¿cuanto se tarda en
evaluar cada sistema? Según la metodología que utilizamos en
Entropycs (pruebas multiperiodo y multimercado) tardamos 30 minutos
en evaluar cada uno, por lo que evaluarlos todos nos llevaría algo
más de 3 años. Evidentemente parece que la idea no es del todo viable. Pero
existe mucho margen para la optimización: mejoras en el propio
código R de evaluación, el uso de procesadores multinúcleo, el
uso de clusters de PCs, o la paralelización mediante tarjetas
gráficas (GPUs). En Entropycs estamos convencidos de que con
relativamente poco esfuerzo podríamos evaluar un sistema en menos de
10 segundos, lo que reduciría el tiempo de evaluación de todos los
sistemas a tan sólo una semana, que es algo más que asumible.
Para el caso de combinaciones de 2
indicadores mediante la fórmula:
<indicador> <op_rel>
<indicador>
Nos llevaría a los 2 millones de
sistemas, que a 10 segundos, tardaríamos casi un año en su
evaluación. Y a más indicadores, el número de sistemas posibles se
dispara. Para los sistemas de la forma:
<indicador> <op_rel>
<indicador> <op_bol> <indicador> <op_rel>
<indicador>
nos vamos a los 7 billones de sistemas
(billones de los europeos). Y para los sistemas de tipo:
<indicador> <op_art>
<indicador> <op_rel> <indicador> <op_bol>
<indicador> <op_art> <indicador> <op_rel>
<indicador>
ya nos vamos a 31*10^18 sistemas.
Imposible de evaluar, ¿no?
Bueno, quizás no tanto. Pensemos en el
caso del ajedrez, donde existen 2*10^116 partidas posibles, y los
programas de ajedrez no lo hacen tan mal (de hecho, hace años que
ningún humano es capaz de ganarle al mejor de los programas). La
supercomputadora de ajedrez Deep Blue era capaz de evaluar 200
millones de jugadas por segundo (y de aquello hace 15 años).
Utilizando estos números, sería cuestión de horas evaluar los
sistemas con 4 indicadores, y los de cinco empezaría a dejar de ser
una utopía (del orden de 4 años).
Y, sobre todo, recordemos lo que dice
la sabiduría popular del trading: “si con 5 indicadores tu sistema
no es rentable, nunca lo será por muchos más indicadores que
añadas”.
jueves, 18 de abril de 2013
Buscando Huecos en los Datos
-->
En una serie de entradas de este blog vimos cómo cargar los datos históricos de forex en una
base de datos relacional, y cómo disponer los datos en este formato
tenía ciertas ventajas a la hora de proceder a su análisis. En esta
ocasión vamos a ver otra de las ventajas de las bases de datos, en
concreto, cómo nos pueden ayudar a la hora de buscar huecos en los
datos históricos.
Todos los ficheros de datos históricos
tienen huecos. A veces, incluso, nos podemos encontrar con saltos que
comprenden varios días, y que pueden llegar a resultar muy
peligrosos, porque pueden distorsionar de forma importante nuestro
análisis. Por tanto, es importante conocer la calidad de los datos
que estamos utilizando en nuestros back tests.
Una vez tengamos los datos en la base
de datos en el formato en el que habíamos propuesto en las entradas
anteriores de nuestro blog, podemos proceder a su análisis.
Si queremos conocer el número de
huecos de una determinada longitud que existen en el histórico de
datos, escribiremos:
SELECT count(*),
((SELECT MIN(e2.unix) FROM eurusd
e2
WHERE e2.unix > e1.unix) - e1.unix - 60) / 60 as minutes
FROM eurusd e1 WHERE minutes = 1
donde 'minutes' es la longitud en
minutos del hueco que estamos analizando. También podríamos ver
concretamente en qué fecha y hora se producen dichos huecos, para
ello escribiríamos:
SELECT DATETIME(e1.date, '+1 minutes')
AS start_date,
DATETIME((SELECT MIN(e2.date) FROM
eurusd e2 WHERE e2.date > e1.date), '-1 minutes') AS end_date,
((SELECT MIN(e2.unix) FROM eurusd
e2 WHERE e2.unix > e1.unix) - e1.unix - 60) / 60 as minutes
FROM eurusd e1 WHERE minutes = 1
o si lo que queremos es sacar una tabla
que nos indique para cada longitud de hueco el número de ellos que
hay, escribiríamos:
SELECT count(*),
((SELECT MIN(e2.unix) FROM eurusd
e2 WHERE e2.unix > e1.unix) - e1.unix - 60) / 60 as minutes
FROM eurusd e1 GROUP BY minutes
Finalmente, a modo de ejemplo voy a
mostrar los resultados de un análisis comparativo realizado sobre
dos históricos de datos, uno de ellos descargado desde la página
web de Forex Tester, y el segundo utilizando el Centro de Historiales
de la herramienta MetaTrader y a través del broker XTB.
El siguiente gráfico muestra para cada
longitud de hueco desde 1 a 30 minutos el número de huecos encontrados:
Como se puede observar, el número de
huecos de longitudes 1, 2, 3 y 4 es tremendamente grande, del orden
de 3 huecos al día para XTB, y de 4 para Forex Tester. Este hecho
debería ser tenido en cuenta por todos aquellos que utilizan
sistemas automáticos que trabajan sobre barras de un minuto, sobre
todo si se basan en optimizaciones y backtests de los mismos.
Etiquetas:
forex tester,
huecos,
metatrader,
SQLite,
xtb
martes, 16 de abril de 2013
Evolución Gramatical, Sistemas de Trading y R (2/2)
-->
En una entrada anterior de este blog vimos lo que era una gramática, y cómo se podían utilizar
las gramáticas para crear automáticamente sistemas de trading. En
esta entrada vamos a ver cómo se puede implementar todo esto con R.
Aunque el código es lo suficientemente genérico para poder utilizar
cualquier gramática, vamos a seguir con nuestro ejemplo de gramática
para la generación de números enteros, para no complicar demasiado
la cosa.
Lo primero que tenemos es que definir
nuestra gramática. Para ello utilizamos una lista de R con las
reglas de producción. Cada elemento de la lista es una regla, y
dentro de cada regla, las distintas opciones van como elementos en un
vector. Seguimos el convenio de encerrar los símbolos no terminales
entre '<' y '>', y dentro de una misma regla de producción
separamos los diferentes tokens mediante espacios (si no lo
hiciésemos así, tendríamos que recurrir a un analizador léxico, y
eso lo complicaría todo). A continuación declaramos el símbolo
inicial, y finalmente dentro de un bucle while() aplicamos las reglas
de producción iterativamente a los símbolos no terminales hasta que
todos los símbolos que queden en la cadena sean terminales.
En este caso hemos utilizado la función
sample() para seleccionar aleatoriamente qué regla de producción
tenemos que aplicar. En un entorno de producción real deberíamos
reemplazar esta regla por algún método de búsqueda inteligente,
tipo algoritmo genético o enjambre de partículas. Pero este es tema
para una futura entrada del blog.
# Definición de la gramática
nt <- list(
entero = c("<entero>
<digito>", "<digito>"),
digito = c("1", "2",
"3", "4", "5", "6", "7",
"8", "9", "0")
)
# Símbolo inicial
programa <- c("<entero>")
fin <- FALSE
while (!fin) {
fin <- TRUE
nprograma <- c()
for(i in 1:length(programa)) {
token <- programa[i]
if( grepl("<.+>",
token) ) {
# Se trata de un símbolo
no terminal, hay que parsearlo
# Quitamos los marcadores <
y >
token <- substr(token,
2, nchar(token)-1)
# Seleccionamos la regla
adecuada
regla <-
nt[names(nt)==token][[1]]
# Seleccionamos
aleatoriamente una producción
prod <- sample(regla, 1)
# Parseamos la producción
a aplicar y la añadimos
nprograma <-
c(nprograma, unlist(strsplit(prod, " ")))
# Tenemos que hacer otra iteración
fin <- FALSE
} else {
# Se trata de un símbolo
terminal, lo añadimos tal cual
nprograma <-
c(nprograma, token)
}
}
programa <- nprograma
}
# Programa final
sistema <- paste(programa,
collapse="")
print(sistema)
Etiquetas:
evolución gramatical,
gramáticas
jueves, 11 de abril de 2013
Evolución Gramatical, Sistemas de Trading y R (1/2)
-->
En una entrada reciente de este blog describí un sistema para la generación automática de
sistemas de trading. Este sistema giraba en torno al concepto de
“Evolución Gramatical”, optimizadores avanzados, y el lenguaje
de análisis estadístico R. Degraciadamente R no dispone de ningún
paquete específicamente diseñado para la optimización de sistemas
basado en evoluciones gramaticales. Sin embargo, implementar los
fundamentos de este sistema no es complicado. En esta serie de
entradas vamos a revisar brevemente qué es eso de la evolución
gramatical, cómo puede ser implementada en R, y qué tiene que ver
con los sistemas de trading.
Las gramáticas son una herramienta muy
conocida en el mundo de la ingeniería informática, y más
concretamente, en el área de compiladores. Las gramáticas nos
permiten definir de manera rigurosa los lenguajes de programación
(como por ejemplo C++ o java), y por tanto, nos permite determinar si
un determinado código fuente está correctamente escrito según el
estándar del lenguaje. Además, las gramáticas también son útiles
a la hora de compilar los programas, es decir, de transformar el
código fuente de un programa en código máquina entendido por el
ordenador.
Sin embargo, en el caso que nos ocupa,
la generación de estrategias de trading, le vamos a dar la vuelta a
la tortilla. Es decir, no utilizamos las gramáticas para decidir si
una estrategia es (léxica y sintácticamente) correcta, sino para
generar automáticamente el código de dicha estrategia. Podríamos,
por ejemplo, definir una gramática que nos permita generar
estrategias basadas en el cruce de medias móviles, y dejar que sea
el propio ordenador el que “pruebe” distintas formas de combinar
las medias móviles en una estrategia. Por ejemplo, el sistema que
utilizamos en Entropycs generaría sistemas como los siguientes:
* SMA(simbolo,6)>SMA(simbolo,4+1)
| SMA(simbolo,92)>SMA(simbolo,90)
* SMA(simbolo,10)>SMA(simbolo,30)
& SMA(simbolo,2)>SMA(simbolo,36)
* SMA(simbolo,116)>SMA(simbolo,99)
Evidentemente la mayoría de los
sistemas así creados no tienen mucho sentido, pero si pensamos que
el ordenador genera y evalúa varios centenares de sistemas por
segundo, podemos hacernos una idea del potencial de este método.
Para describir las gramáticas se
utiliza la denominada la notación Backus-Naur (BNF).
Técnicamente la gramática se compone de un conjunto de símbolos
terminales, un conjunto de símbolos no terminales, unas reglas de
producción, y un símbolo inicial. Aunque suene muy complejo, la
idea es muy sencilla. Veamos un ejemplo. Supongamos que queremos
definir una gramática que genere números enteros de cualquier
longitud. Un número se compone de uno o más dígitos, y los dígitos
son los números de 0 a 9. Por tanto, la gramática sería
<número> := <número><dígito>
| <dígito>
<dígito> := 0 | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9
Los símbolos no terminales son
<número> y <dígito>; los símbolos terminales sería de
0 al 9, y el símbolo inicial sería <número>. Las reglas de
producción son las vistas más arriba y transforman un símbolo no
terminal, a la izquierda del :=, por cualquiera de los símbolos
(terminales o no) de la derecha, que vienen separados por '|'. Notese
que en la generación de sistemas de trading seleccionaríamos al
azar (o según nuestro algoritmo de búsqueda) el símbolo de la
derecha a utilizar.
Por ejemplo, una posible evolución de
la gramática sería:
Paso 1: <número>
Paso 2: <número><dígito>
Paso 3: <número><dígito>5
Paso 4: <dígito>35
Paso 5: 135
Y al ser todos los símbolos ya
terminales, acabaría el proceso. Dejo propuesto al lector como
ejercicio crear una gramática que sume dos números enteros.
¿Y cómo se hace esto en R? Pues eso
es el tema del que tratará nuestra siguiente entrada de blog.
domingo, 7 de abril de 2013
Modificador de Relevancia Estadística
-->
Un modificador más adecuado podría estar basado en una tangente hiperbólica, o en la función logística. Yo me inclino más por la función logística, y en concreto, por la función 1/(1+exp((-n/5)+6)). El valor 6 viene del hecho de que la función logística sólo es interesante en el intervalo [-6,6], ya que para valores mayores de 6 es prácticamente 1, y para valores menores de 6 es prácticamente 0. El valor de 5 se escoge para centrar la función en el valor 30 (5*6) del que antes habíamos hablado. Así el indicador es progresivamente más relevante de 20 a 40 operaciones, para valores menores de 20 se degrada muy rápidamente, y para valores mayores de 40 el modificador apenas si tiene impacto. Justo lo que buscamos. Véase la figura:
Uno de los principales inconvenientes
con los que nos encontramos a la hora de evaluar a priori la
rentabilidad de un sistema de trading es el de la relevancia
estadística de los resultados. De sobra es conocido que para que se
pueda realizar un análisis estadístico sobre un conjunto de datos
es necesario que dicho conjunto tenga un tamaño mínimo. Es difícil
decir exactamente que se entiende por “tamaño mínimo”, pero
parece que el valor más aceptado es el de 30 muestras (o el de 5 por
celda en el caso de trabajar con tablas de contingencia). Si lo que
estamos trabajando es con un sistema de seguimiento de tendencia
basado en el cruce de dos medias móviles, y nuestro backtest se
realiza sobre barras diarias en un periodo de 6 años, es posible que
no lleguemos a ese número mínimo de 30 operaciones (entrada +
salida), sobre todo si las medias móviles se calculan sobre periodos
de tiempo muy largos.
En estos casos lo que se hace es
evaluar a mano el número de operaciones realizadas, y si según
nuestro criterio personal decidimos que es muy bajo, se descarta el
análisis por no ser relevante. Pero si lo que estamos utilizando es
una plataforma de optimización automática de sistemas, la cosa se
complica un poco. Porque, ¿qué hacemos en este caso? ¿simplemente
descartamos aquellas iteraciones que produzcan menos de 30 entradas?
¿no deberían ser tenidas en cuenta también aquellas con 29
entradas? ¿y 28? ¿y 27? ...
Para solucionar este problema propongo
utilizar una especie de modificador de relevancia estadística, que
pondere nuestro indicador de bondad de la estrategia según la
relevancia estadística de los resultados. Este modificador debería
poder ser aplicado indistintamente al indicador de bondad que
habitualmente utilicemos, ya sea beneficio neto, drawdown máximo,
ratio de Sharpe, SQN, etc. Desconozco si en la literatura
estadística, o sobre trading cuantitativo, se ha propuesto alguna
solución similar a la aquí planteada. Si es así, le rogaría a los
lectores escriban un comentario a esta entrada de blog con la
correspondiente referencia.
A priori, lo natural sería utilizar
como modificador de relevancia alguna variación del error estándar
utilizado en los propios análisis estadísticos. A saber,
multiplicar nuestro indicador por 1-1/sqtr(n) (si lo que andamos
buscando es maximizar el indicador). El problema de esta solución es
que con muy pocas operaciones, ya se considera que el indicador es
bastante confiable. Por ejemplo, con 5 operaciones ya tendríamos una
ponderación del 55% sobre el indicador. Véase la siguiente figura:
Un modificador más adecuado podría estar basado en una tangente hiperbólica, o en la función logística. Yo me inclino más por la función logística, y en concreto, por la función 1/(1+exp((-n/5)+6)). El valor 6 viene del hecho de que la función logística sólo es interesante en el intervalo [-6,6], ya que para valores mayores de 6 es prácticamente 1, y para valores menores de 6 es prácticamente 0. El valor de 5 se escoge para centrar la función en el valor 30 (5*6) del que antes habíamos hablado. Así el indicador es progresivamente más relevante de 20 a 40 operaciones, para valores menores de 20 se degrada muy rápidamente, y para valores mayores de 40 el modificador apenas si tiene impacto. Justo lo que buscamos. Véase la figura:
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
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
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;
(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.
Suscribirse a:
Entradas (Atom)