From 606148e19c2a14a31653acc34d7099c9a50dae3b Mon Sep 17 00:00:00 2001 From: fredfortier Date: Tue, 28 Nov 2017 13:42:58 -0500 Subject: [PATCH] DOC: Integrating with ccxt --- catalyst/exchange/ccxt/__init__.py | 0 catalyst/exchange/ccxt/ccxt_exchange.py | 121 ++++++++++++++++++++++++ etc/requirements.txt | 3 + tests/exchange/test_ccxt.py | 101 ++++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 catalyst/exchange/ccxt/__init__.py create mode 100644 catalyst/exchange/ccxt/ccxt_exchange.py create mode 100644 tests/exchange/test_ccxt.py diff --git a/catalyst/exchange/ccxt/__init__.py b/catalyst/exchange/ccxt/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/catalyst/exchange/ccxt/ccxt_exchange.py b/catalyst/exchange/ccxt/ccxt_exchange.py new file mode 100644 index 00000000..86673a1c --- /dev/null +++ b/catalyst/exchange/ccxt/ccxt_exchange.py @@ -0,0 +1,121 @@ +import re +from collections import defaultdict + +import ccxt +import pandas as pd +from logbook import Logger + +from catalyst.constants import LOG_LEVEL +from catalyst.exchange.exchange import Exchange +from catalyst.exchange.exchange_bundle import ExchangeBundle +from catalyst.exchange.exchange_errors import InvalidHistoryFrequencyError + +log = Logger('CCXT', level=LOG_LEVEL) + + +class CCXT(Exchange): + def __init__(self, exchange_name, key, secret, base_currency, + portfolio=None): + log.debug('available exchanges:\n{}'.format(ccxt.exchanges)) + self.api = ccxt.poloniex({ + 'apiKey': key, + 'secret': secret, + }) + markets = self.api.load_markets() + log.debug('the markets:\n{}'.format(markets)) + + self.name = exchange_name + self.assets = {} + self.load_assets() + self.base_currency = base_currency + self._portfolio = portfolio + self.transactions = defaultdict(list) + + self.num_candles_limit = 2000 + self.max_requests_per_minute = 60 + self.request_cpt = dict() + + self.bundle = ExchangeBundle(self.name) + + def account(self): + return None + + def time_skew(self): + return None + + def get_symbol(self, asset): + parts = asset.symbol.split('_') + return '{}/{}'.format(parts[0].upper(), parts[1].upper()) + + def get_timeframe(self, freq): + freq_match = re.match(r'([0-9].*)?(m|M|d|D|h|H|T)', freq, re.M | re.I) + if freq_match: + candle_size = int(freq_match.group(1)) \ + if freq_match.group(1) else 1 + + unit = freq_match.group(2) + + else: + raise InvalidHistoryFrequencyError(frequency=freq) + + if unit.lower() == 'd': + timeframe = '{}d'.format(candle_size) + + elif unit.lower() == 'm' or unit == 'T': + timeframe = '{}m'.format(candle_size) + + elif unit.lower() == 'h' or unit == 'T': + timeframe = '{}h'.format(candle_size) + + return timeframe + + def get_candles(self, freq, assets, bar_count=None, start_dt=None, + end_dt=None): + symbols = self.get_symbols(assets) + timeframe = self.get_timeframe(freq) + delta = start_dt - pd.to_datetime('1970-1-1', utc=True) + ms = int(delta.total_seconds()) * 1000 + + ohlcvs = self.api.fetch_ohlcv( + symbol=symbols[0], + timeframe=timeframe, + since=ms, + limit=bar_count, + params={} + ) + + candles = [] + for ohlcv in ohlcvs: + candles.append(dict( + last_traded=pd.to_datetime(ohlcv[0], unit='ms', utc=True), + open=ohlcv[1], + high=ohlcv[2], + low=ohlcv[3], + close=ohlcv[4], + volume=ohlcv[5] + )) + return candles + + def get_balances(self): + return None + + def create_order(self, asset, amount, is_buy, style): + return None + + def get_open_orders(self, asset): + return None + + def get_order(self, order_id): + return None + + def cancel_order(self, order_param): + return None + + def tickers(self, assets): + return None + + def get_account(self): + return None + + def get_orderbook(self, asset, order_type, limit): + return None diff --git a/etc/requirements.txt b/etc/requirements.txt index aaa129ca..dee52a34 100644 --- a/etc/requirements.txt +++ b/etc/requirements.txt @@ -80,3 +80,6 @@ empyrical==0.2.1 tables==3.3.0 +#Catalyst dependencies +ccxt==1.10.251 + diff --git a/tests/exchange/test_ccxt.py b/tests/exchange/test_ccxt.py new file mode 100644 index 00000000..7fd0dcff --- /dev/null +++ b/tests/exchange/test_ccxt.py @@ -0,0 +1,101 @@ +import os +import tempfile + +import pandas as pd +from catalyst.exchange.ccxt.ccxt_exchange import CCXT +from catalyst.finance.order import Order +from base import BaseExchangeTestCase +from logbook import Logger +from catalyst.exchange.exchange_utils import get_exchange_auth +from catalyst.utils.paths import ensure_directory + +log = Logger('test_ccxt') + + +class TestCCXT(BaseExchangeTestCase): + @classmethod + def setup(self): + exchange_name = 'poloniex' + auth = get_exchange_auth(exchange_name) + self.exchange = CCXT( + exchange_name=exchange_name, + key=auth['key'], + secret=auth['secret'], + base_currency=None, + portfolio=None + ) + + def test_order(self): + log.info('creating order') + asset = self.exchange.get_asset('neo_btc') + order_id = self.exchange.order( + asset=asset, + limit_price=0.0005, + amount=1, + ) + log.info('order created {}'.format(order_id)) + assert order_id is not None + pass + + def test_open_orders(self): + log.info('retrieving open orders') + asset = self.exchange.get_asset('neo_btc') + orders = self.exchange.get_open_orders(asset) + pass + + def test_get_order(self): + log.info('retrieving order') + order = self.exchange.get_order( + u'2c584020-9caf-4af5-bde0-332c0bba17e2') + assert isinstance(order, Order) + pass + + def test_cancel_order(self, ): + log.info('cancel order') + self.exchange.cancel_order(u'dc7bcca2-5219-4145-8848-8a593d2a72f9') + pass + + def test_get_candles(self): + log.info('retrieving candles') + candles = self.exchange.get_candles( + freq='5T', + assets=[self.exchange.get_asset('eth_btc')], + bar_count=200, + start_dt=pd.to_datetime('2017-01-01', utc=True) + ) + + df = pd.DataFrame(candles) + df.set_index('last_traded', drop=True, inplace=True) + + folder = os.path.join( + tempfile.gettempdir(), 'catalyst', self.exchange.name, 'eth_btc' + ) + ensure_directory(folder) + + path = os.path.join(folder, 'output.csv') + df.to_csv(path) + pass + + def test_tickers(self): + log.info('retrieving tickers') + tickers = self.exchange.tickers([ + self.exchange.get_asset('eth_btc'), + self.exchange.get_asset('etc_btc') + ]) + assert len(tickers) == 2 + pass + + def test_get_balances(self): + log.info('testing wallet balances') + balances = self.exchange.get_balances() + pass + + def test_get_account(self): + log.info('testing account data') + pass + + def test_orderbook(self): + log.info('testing order book for bittrex') + asset = self.exchange.get_asset('eth_btc') + orderbook = self.exchange.get_orderbook(asset) + pass