From 985fcb737c733931dd0d05bb9db695e167eb9dd1 Mon Sep 17 00:00:00 2001 From: AuroraTradingTeam Date: Tue, 7 Nov 2017 22:17:33 +0000 Subject: [PATCH] Added historical price query on cryptocompare. --- README.md | 27 +++++++-- libcryptomarket/api/cryptocompare_api.py | 49 ++++++++++++++- libcryptomarket/historical.py | 77 ++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 libcryptomarket/historical.py diff --git a/README.md b/README.md index ec383ae..e5e4d33 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # libcryptomarket: Powerful cryptocurrency market analysis toolkit -------------------- - ## Objective The library is for researchers to analysis cryptocurrency in a fast and -flexible way. Currently there are different source of API to get the +flexible way. Currently there are different source of API to get the cryptocurrency market information. The sources are from websites which provides -a general comparative information among the currencies and from exchanges. +a general comparative information among the currencies and from exchanges. The +target is to normalize the API functions from data source, and let the users +query the data without pain. ## Prerequisite @@ -37,8 +37,25 @@ from libcryptomarket.instrument import get_instruments instruments = get_instruments() ``` +### Historical + +Run + +``` +from libcryptomarket.historical import get_historical_prices + +prices = get_historical_prices(symbol='LTCBTC', + exchange='Poloniex', + period="hour", + limit=4000).set_index(['r_time']) +``` + +Then you can get historical price in ascending order seamlessly, even though +the limit has exceeded the source limit. The application helps continue +querying until the data reaches the requirements. + ## Contribution The project is targeting as a core but generic toolkit to query cryptocurrency -market, so we are happy if you join to contribute and make it better. Please +market, so we are happy if you join to contribute and make it better. Please do not hesitate to contact us (gavincyi at gmail dot com). \ No newline at end of file diff --git a/libcryptomarket/api/cryptocompare_api.py b/libcryptomarket/api/cryptocompare_api.py index 6a4c674..22733cf 100644 --- a/libcryptomarket/api/cryptocompare_api.py +++ b/libcryptomarket/api/cryptocompare_api.py @@ -1,7 +1,9 @@ #!/bin/python import requests +import json +from datetime import datetime -API_URL = "https://www.cryptocompare.com/api/data/" +API_URL = "https://min-api.cryptocompare.com/data/" class CryptocompareCoinlist: @@ -30,9 +32,54 @@ class CryptocompareCoinlist: self.r_url = kwargs['Url'] or '' +class CryptocompareHisto: + """Cryptocompare histo. + """ + + def __init__(self, **kwargs): + """Constructor. + """ + self.r_close = float(kwargs['close']) + self.r_high = float(kwargs['high']) + self.r_low = float(kwargs['low']) + self.r_open = float(kwargs['open']) + self.r_time = datetime.fromtimestamp(int(kwargs['time'])) + self.r_volumefrom = float(kwargs['volumefrom']) + self.r_volumeto = float(kwargs['volumeto']) + + def get_coinlist(): """Return general info for all coins available. """ url = API_URL + "coinlist" return requests.get(url).json() + +def get_histo(period, fsym, tsym, e, limit=None, toTs=None): + """Return historical prices. + + :param period: Period, one of the values of "minute", "hour" and "day". + :param fsym: From symbol. + :param tsym: To symbol. + :param e: Exchange name. + :param limit: Limit of return data. Default is None. + :param toTs: To timestamp. Default is None. + """ + valid_list = ["minute", "hour", "day"] + if period not in valid_list: + raise ValueError("Period must be in {0}".format(', '.join(valid_list))) + + url = API_URL + "histo" + period + params = { + "fsym": fsym, + "tsym": tsym, + "e": e + } + + if limit is not None: + params["limit"] = limit + + if toTs is not None: + params["toTs"] = toTs + + return requests.get(url, params=params).json() diff --git a/libcryptomarket/historical.py b/libcryptomarket/historical.py new file mode 100644 index 0000000..b45cea4 --- /dev/null +++ b/libcryptomarket/historical.py @@ -0,0 +1,77 @@ +from functools import partial + +import pandas as pd + +from libcryptomarket.api.cryptocompare_api import ( + get_histo, CryptocompareHisto +) + +def get_historical_prices(source='cryptocompare', symbol=None, exchange=None, + period=None, limit=0, from_time=None, to_time=None): + """Get historical prices. + + :param source: Source of data. + :param symbol: Symbol. Default is None. + :param exchange: Exchange. Default is None. + :param period: Data frequency. Default is None, which follows the source + default value. + :param limit: Limit of records. Default is 0, which follows the source + default value. + :param from_time: From time. Default is None, which follows the source + default value. + :param to_time: To time. Default is None, which follows the source default + value. + """ + if source == 'cryptocompare': + if period is None: + raise ValueError("Input parameter period cannot be None.") + + if exchange is None: + raise ValueError("Input parameter exchange cannot be None.") + + if ((limit > 0) + + ((from_time is not None) or (to_time is not None)) > 1): + raise ValuError("Only accept input parameter limit, or from_time" + " and to_time pair.") + + # Parse from (first 3) and to (last 3) symbol from the parameter + # symbol. + from_sym = symbol[:3] + to_sym = symbol[3:6] + + func = partial(get_histo, period=period, fsym=from_sym, tsym=to_sym, + e=exchange) + + data = [] + if limit > 0: + to_time = 0 + + while limit > 0: + if to_time == 0: + response = func(limit=limit)['Data'] + else: + response = func(limit=limit, toTs=to_time)['Data'] + + if len(response) == 0: + # Terminate if no further response + limit = 0 + else: + data += response + limit -= len(response) + to_time = response[0]['time'] - 1 + + elif from_time is not None or to_time is not None: + raise NotImplementedError() + else: + data += func()['Data'] + + data = pd.DataFrame([CryptocompareHisto(**e).__dict__ for e in data]) + return data.sort_values(['r_time']) + else: + raise ValueError("No source is called {0}".format(source)) + + + + + +