Arctic es una libreria que nos proporciona junto a MongoDB, una base de datos de altas prestaciones para el almacenamiento de series temporales. Todos los que manipulamos series temporales, ya sean informacion de precio de acciones, futuros, strikes de las opciones, indicadores macros, tipos de interes, o lo que sea, las obtenemos de diferentes fuentes, y necesitamos diferentes recursos para poder obtener precios, manipularlos, trabajarlos y ponerlos en comun, haciendo un poco mas tediosa la tarea. Una gran idea, es el centralizar todo en una misma base de datos, que nos proporcione rapidez y robustez a la hora de hacer dichos cometidos.

Arctic utiliza MongoDB como base de datos subyacente, ademas de utilizar la compresion LZ4, que puede llamar a cientos de millones de filas por segundo. Siendo optimo para el manejo de grandes series temporales.

Esta libreria a sido desarollada por el grupo de hedge funds Man Group, para dar respuesta al problema previamente planteado, el de solucionar como lidiar con y esta totalmente implementada en AHL, Numeric, GLG, FRM entre muchas otras..

Si deseas hacer analisis con bigdata, y hacer mas eficientes tus procesos, a la hora de manipular grandes time sereies sets, Arctic es tu aliado! En el pasado Dask fue una buena alternativa, pero ha llegado el momento de decirle adios para estas labores, Arctic ha llegado para quedarse.

Introduccion

arctic python

Cuando utilizamos Arctic para manipular grandes datasets de series temporales, nos ayuda a tener mayor velocidad en lectura y escritura, ademas de cargarlos de una forma mas rápida dentro de DataFrames. Es una librería muy sencilla de configurar y de utilizar, y puede incrementar nuestra productividad, evitándonos tener que lidiar con este problema de formas menos eficientes.

Para conocer un poco mas a fondo la tecnologia subyacente detras de Arctic, os recomiendo este libro de James Blackburn, donde presentaba lo que era Arctic a la comunidad.

James Blackburn at All Your Base 2015 from LeadDev on Vimeo.

Utilizando Arctic para importar CSV

Para este lab, vamos a utilizar la base de datos de Norgate de futuros continuos, y basicamente, vamos a cargarlos todos en Arctic, para poder disponer de ellos, de una forma rapida, cuando sean necesarios.

El primer paso, es importar todas las librerias necesarias

import pandas as pd
import glob
import os
import time
from arctic import Arctic
os.chdir('Continuous Futures//')

Una vez, con todas las librerias listas, vamos a conectar con nuestra base de datos MongoDB, la cual tenemos instalado en localhost. Posteriormente, crearemos una nueva libreria, y la asignaremos a la variable library

store = Arctic('localhost')
store.initialize_library('ThePythonLAB_continious_futures')
library = store['ThePythonLAB_continious_futures']

En el siguiente paso, desde la carpeta que hemos asignado posteriormente en el os.chir, vamos a buscar todos los archivos, que tengan un formato .csv, y vamos a guardar su nombre en una lista.

csv_files = glob.glob('*.csv')
csv_files[0:10]
1

Una vez ya tenemos una lista, con todos los nombres de los archivos que queremos importar a la base de datos, creamos un bucle for, para cada uno de los archivos, donde solo elegiremos los historicos los ajustados mediante la metodologia de Norgate CCB, y se leen, se ajusta la columna date a DateTime para su posterior uso como DateTimeIndex, extraeremos informacion para los metadatos, añadiremos la columna Date como indice de nuestro DataFrame, seleccionando unicamente las columnas que queremos importar a nuestra base de datos en Arctic, para finalmente escribirlas en un DataFrame.

for file in csv_files:
    if file[-7:-4] == 'CCB':
        df = pd.read_csv(file)
        df['Date'] =pd.to_datetime(df['Date'].astype(str), format='%Y/%m/%d')
        meta_symbol = df['Symbol'].iloc[-1]
        meta_name = df['Security Name'].iloc[-1]
        meta_name = meta_name.replace('.',' ')
        df = df.set_index(df['Date'])
        cols = ['Open','High','Low','Close','Volume','Time']
        df = df[cols]

    try:
        library.write(meta_symbol,df,metadata={'Source':'Norgate',
                                               'Ticker':meta_symbol,
                                               'Name': meta_name,
                                               'Last' : str(df.index[-1])})
    except:
        print(file+str(" Error, no se ha podido añadir"))

Vamos a listar todos los simbolos que hemos cargado, simplemente utilizando la funcion list_simbols() sobre nuestra libreria

library.list_symbols()
2

Para comprobar la metadata que hemos generado cuando cargabamos los tickers, se crea un bucle for para leer y printear la metadata

for i in library.list_symbols():
     item = library.read(i)
     item = item.metadata
     print(item)
3

para cargar un ticker en un dataframe seria tan simple como

data = library.read('&ZT_CCB').data
data
4

Comparacion de rendimiento.

Comparamos , una de las acciones mas cotidianas a la hora de leer datasets en pandas, como es el read_csv(), contra PyMongo Querry, los SQLAlchemy querry, y Arctic, utilizando un archivo de 160MB en todos los casos.

  • Pandas read_csv : ~4.6 seconds
  • PyMongo query: ~28 seconds
  • SQLite query: ~30 seconds
  • Arctic read: ~1.45 seconds

Ademas de ser el formato que de forma mas eficiente almacena la informacion, pues como podemos compararar en el siguiente ejemplo, es casi igual de pesado que el original.

  • Plain CSV file: 160.8 MB
  • MongoDB collection: 347.31 MB
  • SQLite: 297.9 MB
  • Arctic: 160.59 MB

Pero lo que realmente nos interesa es saber cuanto tardaria en toda la base de datos y para ello, lo haremos mediante este bucle for

start = time.time()
rows_read = 0
for s in library.list_symbols():
    rows_read += len(library.read(s).data)
print("Simbolos procesados: %s Filas leidas: %s  Tiempo: %s  Filas por segundo: %s" % (len(library.list_symbols()),
                                                        rows_read,
                                                        (time.time() - start),
                                                        rows_read / (time.time() - start)))
5

El tiempo de lectura de los 111 simbolos, con 807230 filas, ha tardado 0.57 segundos, leyendo unas 1413140.1 filas por segundo. Una autentica barbaridad.

Para simplificar la carga de datos, creamos una funcion, que desde una lista, nos obtenga los closes, y nos lo devuelva todo en un unico DataFrame para todos los tickers.

def df_closes(lista,dropna=False):
    df = pd.DataFrame()
    for ticker in lista:
        temp = library.read(ticker).data
        print(library.read(ticker).metadata)
        df[ticker] = temp['Close']
    if dropna==True:
        df = df.dropna()
            
    return df

Definimos la lista de tickers que queremos tener informacion del precio, y llamamos a la funcion, creando un DataFrame sin que elimine los dropna

tickers = ['&SP_CCB','&GC_CCB','&ZF_CCB']

a = df_closes(tickers,dropna=False)
6

Ploteamos los datos, para comprobar la integridad de los mismos

7