Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
1 Introduccin
El entorno R es extremadamente rpido analizando datos siempre y cuando estos quepan en la
memoria de nuestro ordenador, pero si esto no es as, el rendimiento de R se puede degradar
hasta niveles inaceptables. Existen mltiples soluciones a este problema, como los paquetes
bigMemory1 o ff2, que nos permiten trabajar con grandes volmenes de datos. Sin embargo, para
el caso de anlisis de datos de Forex, se nos da la situacin de que aunque hay que analizar un
gran volumen de datos, es rara la situacin en la que necesitemos tener cargados en memoria la
totalidad de los datos. Es decir, que podemos ir cargando y descargando los datos segn los
vayamos necesitando.
Para este tipo de casos una solucin ms conveniente podra ser utilizar una base de datos
externa, en la que almacenamos la totalidad de los datos, y a continuacin, desde R, los vamos
consultando segn los vayamos necesitando para nuestro anlisis.
En este Informe Tcnico vamos a aprender cmo se pueden analizar grandes volmenes de datos
desde R con la ayuda de SQLite3. Esto nos ser de gran ayuda a la hora de analizar y optimizar
estrategias de trading.
SQLite es un sistema gestor de bases de datos relacionales muy popular y ampliamente utilizado
(de hecho, Bloomberg es uno de sus patrocinadores). SQLite es una librera que se distribuye de
manera gratuita, y cuyo cdigo fuente est disponible para su integracin en otros programas.
SQLite no necesita de ningn tipo de configuracin o de mantenimiento, y actualmente est
siendo utilizada para gestionar bases de datos de varios gigabytes (suficiente para trabajar con
barras de 1 minuto, aunque quizs se quede algo corta para trabajar con datos tick a tick).
1 http://www.bigmemory.org
2 http://ff.r-forge.r-project.org
3 Http://www.sqlite.og
sqlite3 eurusd.db
Nuevamente, desde un punto de vista formal hubiera sido ms correcto crear una nica tabla
para almacenar los datos de todos los smbolos, pero por cuestiones de rendimiento, tenemos
que optar por crear un nico fichero que contiene una nica tabla para cada uno de los
smbolos con los que queramos trabajar.
<TICKER>,<DTYYYYMMDD>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>
EURUSD,20010102,230100,0.9507,0.9507,0.9507,0.9507,4
EURUSD,20010102,230200,0.9506,0.9506,0.9505,0.9505,4
EURUSD,20010102,230300,0.9505,0.9507,0.9505,0.9506,4
...
Este formato no es el que SQLite espera, por lo que hay que hacer un pre-procesamiento
del mismo. Aqu nos encontramos con el problema de que necesitamos un editor de
textos que sea lo suficientemente potente como para poder cargar este fichero de datos
en memoria, y que sea lo suficientemente verstil para permitir hacer cambios
complejos en el mismo. En el mundo Linux tenemos el editor vi que cumple ambas
funciones (existe una versin para Windows de este editor de textos llamada Vim).
Alternativamente, aquellos que tengan conocimientos de programacin pueden escribir
un pequeo script en Perl, Python, TCL o similar, que haga la tarea.
As que editamos el fichero con vi, borramos la primera lnea y escribimos la siguiente
orden:
:1,$s/\w\{6},\(\d\{4}\)\(\d\{2}\)\(\d\{2}\),\(\d\{2}\)\(\d\{2}\)\(\d\
{2}\),\(.*\),\d$/\1-\2-\3 \4:\5,0,\7/
2001-01-02 23:01,0,0.9507,0.9507,0.9507,0.9507
1 http://www.forextester.com/data/datasources.html
1971.01.27,00:00,0.5386,0.5386,0.5386,0.5386,1
:1,$s/\(\d\{4}\)\.\(\d\{2}\)\.\(\d\{2}\),\(\d\{2}:\d\{2}\),\(.*\),\d\
{1,5}$/\1-\2-\3 \4,0,\5/
.mode csv
.import EURUSD.csv eurusd
Esto nos importa la totalidad de los datos de las barras en nuestra tabla. Tan slo nos
quedara corregir el campo de tiempo en formato unix, y para ello escribimos:
.echo on
.mode column
.headers on
.nullvalue NULL
Bsicamente, lo que le hemos dicho a SQLite es que queremos ver los datos formateados por
columnas, y con una cabecera de tabla.
Para mostrar todos los elementos contenidos en la base de datos (que recomiendo no hacer
porque llevara un rato largo visualizarlos, ya que son muchos) se hara:
que por cierto, debe coincidir con el nmero de lneas del fichero CSV desde el que hemos
importado los datos, y que se pueden contar con la orden Unix:
wc -l EURUSD.csv
INSERT INTO eurusd (date, unix, open, high, low, close) VALUES (
'2011-11-01 00:01:00', 0, '1.1212', '1.2323', '1.3434', '1.4545');
Finalmente, podemos consultar los datos de cualquier barra con una orden como:
Si queremos ver el mximo y mnimo alcanzado durante un periodo cualquiera de tiempo, por
ejemplo 40 minutos, hacemos:
SELECT high, low FROM eurusd WHERE date >= "2001-01-05 02:00" AND date <=
"2001-01-05 02:40";
(SELECT MIN(date)
FROM eurusd
WHERE date > '2010-01-01' AND date < '2011-01-01'
GROUP BY STRFTIME("%Y-%m-%d", date));
Es decir, podemos agrupar los datos en barras de cualquier longitud, y no slo las longitudes
estndar de 5 minutos, 15 minutos, 1 hora, etc. que ofrecen la mayora de las plataformas de
trading.
Uniendo el ejemplo anterior con las consultas de apertura, mximo, mnimo y cierre de la
seccin anterior, 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. Adems, tambin
podemos identificar mejor el tamao de barra ptimo para nuestras estrategias de trading,
optimizndolas para cada una las longitudes de barra, desde 1 minuto, hasta valores como
1440 minutos (1 da).
Por ejemplo, para ver los datos agrupados en barras de 13 minutos utilizamos:
Desgraciadamente esta consulta slo 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, tendramos que utilizar otra consulta algo ms compleja:
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 anlisis de tipo walk forward, que requieren de mltiples consultas.
Pero aqu es donde entra en juego las optimizaciones que permite SQLite, y sobre todo, ese
misterioso atributo unix de nuestra tabla, y que veremos en la siguiente seccin.
5 Bsqueda de Huecos
Como hemos mencionado anteriormente, cargar los datos histricos de forex en una base de
datos relacional tiene ciertas ventajas a la hora de proceder a su anlisis. En esta seccin vamos a
estudiar una de esas ventajas, en concreto, cmo la base de datos nos puede ayudar a la hora de
buscar huecos en los datos histricos.
Todos los ficheros de datos histricos tienen huecos. A veces, incluso, nos podemos encontrar
con saltos que comprenden varios das, y que pueden llegar a resultar muy peligrosos, porque
pueden distorsionar de forma importante nuestro anlisis. Por tanto, es importante conocer la
calidad de los datos que estamos utilizando en nuestros back-tests.
Si queremos conocer el nmero de huecos de una determinada longitud que existen en el
histrico 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. Tambin podramos
ver concretamente en qu fecha y hora se producen dichos huecos, para ello escribiramos:
o si lo que queremos es sacar una tabla que nos indique para cada longitud de hueco el nmero
de ellos que hay, escribiramos:
Finalmente, a modo de ejemplo voy a mostrar los resultados de un anlisis comparativo realizado
sobre dos histricos de datos, uno de ellos descargado desde la pgina web de Forex Tester, y el
segundo utilizando el Centro de Historiales de la herramienta MetaTrader y a travs del broker
XTB.
El siguiente grfico muestra para cada longitud de hueco desde 1 a 30 minutos el nmero de
huecos encontrados:
6 Integracin de SQLite y R
En este apartado vamos a ver cmo 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 librera que debe ser previamente cargada en R:
library(DBI)
library(RSQLite)
A continuacin abrimos una conexin con la base de datos. Para ello primero tenemos que crear
un driver que nos gestione la conexin, y despus abrir la conexin propiamente dicha con el
fichero donde se encuentre la base de datos:
En este momento podemos enviar nuestras consultas SQL a la base de datos, como por ejemplo:
Para recuperar los datos tenemos que utilizar la funcin fetch(), a la que le debemos indicar
cuantas barras queremos recuperar, o -1 para recuperar la totalidad:
En este momento la variable EURUSD contiene los datos listos para ser analizados.
Cuando no necesitemos recuperar ms datos de la consulta, debemos liberar esta con:
dbClearResult(rs)
dbDisconnect(db)
7 Utilidad de Acceso
En este ltimo apartado vamos a agrupar todo lo visto hasta ahora en una nica funcin R, que
nos podr de ser de gran ayuda en nuestros propios anlisis de datos de Forex.
La funcin, llamada symbolQuery, abre la base de datos que contiene los datos Forex, consulta el
rango de datos que le hayamos proporcionado, y nos retorna un objeto tipo xts con los resultados.
La funcin recibe los siguientes parmetros:
symbol: el smbolo que queremos consultar, que debe coincidir con el nombre del
fichero que contiene la base de datos (sin la extensin .db), y adems, debe coincidir con
el nombre de la tabla dentro de dicha base de datos.
from: da y hora desde la que queremos recuperar datos, en el formato YYYY-MM-DD
HH:MM
to: da y hora hasta la que queremos recuperar datos, igualmente en el formato YYYY-
MM-DD HH:MM
minutes: el tamao de la barra deseado, en minutos.
Por ejemplo, para recuperar un ao de datos del cambio EUR/USD en barras de 48 minutos
escribiramos:
# Construimos la consulta
sec <- minutes * 60
sql <- paste("SELECT STRFTIME('%Y-%m-%d %H %M', MIN(date)) AS Date, ",
"(SELECT open FROM ", symbol, " e2 ",
"WHERE e2.unix >= e1.unix / ", sec, " * ", sec, " ",
"ORDER BY e2.unix ASC LIMIT 1) AS Open, ",
return(xret)
}