Skip to content

Commit

Permalink
Merge pull request #29 from lagerfeuer/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
lagerfeuer committed Jan 25, 2020
2 parents 041e8fd + 646c750 commit eea7a16
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 57 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ jobs:
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Test with mypy
run: |
python -m mypy cryptocompare/cryptocompare.py
- name: Test with pytest
run: |
python -m pytest --cov=cryptocompare tests/
Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
all: sdist upload
all: sdist

sdist:
python3 setup.py sdist

upload:
twine upload dist/*

mypy:
python3 -m mypy cryptocompare/cryptocompare.py

test:
python3 -m pytest --cov=cryptocompare
python3 -m pytest --mypy --cov=cryptocompare tests/

.PHONY: sdist upload test
.PHONY: sdist upload mypy test
185 changes: 132 additions & 53 deletions cryptocompare/cryptocompare.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
"""
CryptoCompare API wrapper
"""
import requests
import time
import datetime
import typing
from typing import Union, Optional, List, Dict
Timestamp = Union[datetime.datetime, int, float]

# API
URL_COIN_LIST = 'https://www.cryptocompare.com/api/data/coinlist/'
URL_PRICE = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms={}&tsyms={}'
URL_PRICE_MULTI = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms={}&tsyms={}'
URL_PRICE_MULTI_FULL = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms={}&tsyms={}'
URL_HIST_PRICE = 'https://min-api.cryptocompare.com/data/pricehistorical?fsym={}&tsyms={}&ts={}&e={}'
URL_HIST_PRICE_DAY = 'https://min-api.cryptocompare.com/data/histoday?fsym={}&tsym={}&limit={}'
URL_HIST_PRICE_HOUR = 'https://min-api.cryptocompare.com/data/histohour?fsym={}&tsym={}&limit={}'
URL_HIST_PRICE_MINUTE = 'https://min-api.cryptocompare.com/data/histominute?fsym={}&tsym={}&limit={}'
URL_AVG = 'https://min-api.cryptocompare.com/data/generateAvg?fsym={}&tsym={}&e={}'
URL_EXCHANGES = 'https://www.cryptocompare.com/api/data/exchanges'

# FIELDS
PRICE = 'PRICE'
HIGH = 'HIGH24HOUR'
LOW = 'LOW24HOUR'
VOLUME = 'VOLUME24HOUR'
CHANGE = 'CHANGE24HOUR'
CHANGE_PERCENT = 'CHANGEPCT24HOUR'
MARKETCAP = 'MKTCAP'
_URL_COIN_LIST = 'https://www.cryptocompare.com/api/data/coinlist/'
_URL_PRICE = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms={}&tsyms={}'
_URL_PRICE_MULTI = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms={}&tsyms={}'
_URL_PRICE_MULTI_FULL = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms={}&tsyms={}'
_URL_HIST_PRICE = 'https://min-api.cryptocompare.com/data/pricehistorical?fsym={}&tsyms={}&ts={}&e={}'
_URL_HIST_PRICE_DAY = 'https://min-api.cryptocompare.com/data/histoday?fsym={}&tsym={}&limit={}'
_URL_HIST_PRICE_HOUR = 'https://min-api.cryptocompare.com/data/histohour?fsym={}&tsym={}&limit={}'
_URL_HIST_PRICE_MINUTE = 'https://min-api.cryptocompare.com/data/histominute?fsym={}&tsym={}&limit={}'
_URL_AVG = 'https://min-api.cryptocompare.com/data/generateAvg?fsym={}&tsym={}&e={}'
_URL_EXCHANGES = 'https://www.cryptocompare.com/api/data/exchanges'

# DEFAULTS
CURR = 'EUR'
LIMIT = 1440
###############################################################################


def query_cryptocompare(url, errorCheck=True):
def _query_cryptocompare(url: str, errorCheck: bool = True) -> Optional[Dict]:
"""
Query the url and return the result or None on failure.
:param url: the url
:param errorCheck: run extra error checks (default: True)
:returns: respones, or nothing if errorCheck=True
"""
try:
response = requests.get(url).json()
except Exception as e:
Expand All @@ -41,72 +45,147 @@ def query_cryptocompare(url, errorCheck=True):
return response


def format_parameter(parameter):
def _format_parameter(parameter: object) -> str:
"""
Format the parameter depending on its type and return
the string representation accepted by the API.
:param parameter: parameter to format
"""
if isinstance(parameter, list):
return ','.join(parameter)
else:
return parameter
return str(parameter)

###############################################################################


def get_coin_list(format=False):
response = query_cryptocompare(URL_COIN_LIST, False)['Data']
if format:
return list(response.keys())
else:
return response
def get_coin_list(format: bool = False) -> Union[Dict, List, None]:
"""
Get the coin list (all available coins).
:param format: format as python list (default: False)
:returns: dict or list of available coins
"""
response = _query_cryptocompare(_URL_COIN_LIST, False)
if response:
response = typing.cast(Dict, response['Data'])
return list(response.keys()) if format else response
return None

# TODO: add option to filter json response according to a list of fields


def get_price(coin, curr=CURR, full=False):
def get_price(coin: str, curr: str = CURR, full: bool = False) -> Optional[Dict]:
"""
Get the current price of a coin in a given currency.
:param coin: symbolic name of the coin (e.g. BTC)
:param curr: short hand description of the currency (e.g. EUR)
:param full: full response or just the price (default: False)
:returns: dict of coin and currency price pairs
"""
if full:
return query_cryptocompare(URL_PRICE_MULTI_FULL.format(format_parameter(coin),
format_parameter(curr)))
return _query_cryptocompare(_URL_PRICE_MULTI_FULL.format(_format_parameter(coin),
_format_parameter(curr)))
if isinstance(coin, list):
return query_cryptocompare(URL_PRICE_MULTI.format(format_parameter(coin),
format_parameter(curr)))
return _query_cryptocompare(_URL_PRICE_MULTI.format(_format_parameter(coin),
_format_parameter(curr)))
else:
return query_cryptocompare(URL_PRICE.format(coin, format_parameter(curr)))
return _query_cryptocompare(_URL_PRICE.format(coin, _format_parameter(curr)))


def get_historical_price(coin: str, curr: str = CURR, timestamp: Timestamp = time.time(),
exchange: str = 'CCCAGG') -> Optional[Dict]:
"""
Get the price of a coin in a given currency during a specific time.
def get_historical_price(coin, curr=CURR, timestamp=time.time(), exchange='CCCAGG'):
:param coin: symbolic name of the coin (e.g. BTC)
:param curr: short hand description of the currency (e.g. EUR)
:param timestamp: point in time
:param exchange: the exchange to use
:returns: dict of coin and currency price pairs
"""
if isinstance(timestamp, datetime.datetime):
timestamp = time.mktime(timestamp.timetuple())
return query_cryptocompare(URL_HIST_PRICE.format(coin, format_parameter(curr),
int(timestamp), format_parameter(exchange)))


def get_historical_price_day(coin, curr=CURR, limit=LIMIT):
response = query_cryptocompare(
URL_HIST_PRICE_DAY.format(coin, format_parameter(curr), limit))
return _query_cryptocompare(_URL_HIST_PRICE.format(coin, _format_parameter(curr),
int(timestamp), _format_parameter(exchange)))


# TODO add toTs timestamp
def get_historical_price_day(coin: str, curr: str = CURR, limit: int = LIMIT) -> Optional[Dict]:
"""
Get historical price (day).
:param coin: symbolic name of the coin (e.g. BTC)
:param curr: short hand description of the currency (e.g. EUR)
:param limit: number of data points
:returns: dict of coin and currency price pairs
"""
response = _query_cryptocompare(
_URL_HIST_PRICE_DAY.format(coin, _format_parameter(curr), limit))
if response:
return response['Data']
return None


# TODO add toTs timestamp
def get_historical_price_hour(coin: str, curr: str = CURR, limit: int = LIMIT) -> Optional[Dict]:
"""
Get historical price (hourly).
def get_historical_price_hour(coin, curr=CURR, limit=LIMIT):
response = query_cryptocompare(
URL_HIST_PRICE_HOUR.format(coin, format_parameter(curr), limit))
:param coin: symbolic name of the coin (e.g. BTC)
:param curr: short hand description of the currency (e.g. EUR)
:param limit: number of data points
:returns: dict of coin and currency price pairs
"""
response = _query_cryptocompare(
_URL_HIST_PRICE_HOUR.format(coin, _format_parameter(curr), limit))
if response:
return response['Data']
return None


def get_historical_price_minute(coin, curr=CURR, limit=LIMIT):
response = query_cryptocompare(
URL_HIST_PRICE_MINUTE.format(coin, format_parameter(curr), limit))
# TODO add toTs timestamp
def get_historical_price_minute(coin: str, curr: str = CURR, limit: int = LIMIT) -> Optional[Dict]:
"""
Get historical price (minute).
:param coin: symbolic name of the coin (e.g. BTC)
:param curr: short hand description of the currency (e.g. EUR)
:param limit: number of data points
:returns: dict of coin and currency price pairs
"""
response = _query_cryptocompare(
_URL_HIST_PRICE_MINUTE.format(coin, _format_parameter(curr), limit))
if response:
return response['Data']
return None


def get_avg(coin: str, curr: str = CURR, exchange: str = 'CCCAGG') -> Optional[Dict]:
"""
Get the average price
def get_avg(coin, curr=CURR, exchange='CCCAGG'):
response = query_cryptocompare(URL_AVG.format(
coin, curr, format_parameter(exchange)))
:param coin: symbolic name of the coin (e.g. BTC)
:param curr: short hand description of the currency (e.g. EUR)
:param exchange: exchange to use (default: 'CCCAGG')
:returns: dict of coin and currency price pairs
"""
response = _query_cryptocompare(_URL_AVG.format(
coin, curr, _format_parameter(exchange)))
if response:
return response['RAW']
return None


def get_exchanges() -> Optional[Dict]:
"""
Get the list of available exchanges.
def get_exchanges():
response = query_cryptocompare(URL_EXCHANGES)
:returns: list of available exchanges
"""
response = _query_cryptocompare(_URL_EXCHANGES)
if response:
return response['Data']
return None
7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ autopep8==1.5
certifi==2019.11.28
chardet==3.0.4
coverage==5.0.3
coveralls==1.10.0
doc8==0.8.0
docopt==0.6.2
docutils==0.16
idna==2.8
isort==4.3.21
lazy-object-proxy==1.4.3
mccabe==0.6.1
more-itertools==8.1.0
mypy==0.761
mypy-extensions==0.4.3
packaging==20.1
pbr==5.4.4
pluggy==0.13.1
Expand All @@ -20,10 +24,13 @@ pylint==2.4.4
pyparsing==2.4.6
pytest==5.3.4
pytest-cov==2.8.1
pytest-mypy==0.4.2
requests==2.22.0
restructuredtext-lint==1.3.0
six==1.14.0
stevedore==1.31.0
typed-ast==1.4.1
typing-extensions==3.7.4.1
urllib3==1.25.8
wcwidth==0.1.8
wrapt==1.11.2
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='cryptocompare',
version='0.6.6',
version='0.7.0',
description='Wrapper for CryptoCompare.com',
long_description=readme,
long_description_content_type='text/markdown',
Expand Down

0 comments on commit eea7a16

Please sign in to comment.