From 4eb8a6eb0f9f59632bdfafa1aed806aa0ca03633 Mon Sep 17 00:00:00 2001 From: fredfortier Date: Wed, 29 Nov 2017 22:31:44 -0500 Subject: [PATCH] BLD: populating assets with help from CCXT --- catalyst/assets/_assets.pyx | 36 +++++++++++--- catalyst/examples/mean_reversion_simple.py | 8 ++-- catalyst/exchange/ccxt/ccxt_exchange.py | 56 +++++++++++++++++++++- catalyst/exchange/exchange_utils.py | 19 ++++++++ tests/exchange/test_ccxt.py | 3 ++ 5 files changed, 110 insertions(+), 12 deletions(-) diff --git a/catalyst/assets/_assets.pyx b/catalyst/assets/_assets.pyx index 3537493f..e78e3af1 100644 --- a/catalyst/assets/_assets.pyx +++ b/catalyst/assets/_assets.pyx @@ -401,6 +401,10 @@ cdef class TradingPair(Asset): cdef readonly object end_daily cdef readonly object end_minute cdef readonly object exchange_symbol + cdef readonly float maker + cdef readonly float taker + cdef readonly int trading_state + cdef readonly object data_source _kwargnames = frozenset({ 'sid', @@ -418,7 +422,11 @@ cdef class TradingPair(Asset): 'end_daily', 'end_minute', 'exchange_symbol', - 'min_trade_size' + 'min_trade_size', + 'maker', + 'taker', + 'trading_state', + 'data_source' }) def __init__(self, object symbol, @@ -434,10 +442,14 @@ cdef class TradingPair(Asset): object first_traded=None, object auto_close_date=None, object exchange_full=None, - object min_trade_size=None): + float min_trade_size=0.000001, + float maker=0.0015, + float taker=0.0025, + int trading_state=0, + object data_source='catalyst'): """ Replicates the Asset constructor with some built-in conventions - and a new 'leverage' attribute. + and adds properties for leverage and fees. Symbol ------ @@ -469,8 +481,6 @@ cdef class TradingPair(Asset): highest volume and market cap generally benefit from high leverage. New currencies from ICO generally cannot be leveraged. - The leverage value is either None or and integer. - Leverage allows you to open a larger position with a smaller amount of funds. For example, if you open a $5,000 position in BTC/USD with 5:1 leverage, only one-fifth of this amount, or $1000, will be @@ -480,6 +490,11 @@ cdef class TradingPair(Asset): the position. If you open with 1:1 leverage, $5,000 of your balance will be tied to the position. + Fees + ---- + Exchanges generally charge a taker (taking from the order book) or + maker (adding to the order book) fee. + :param symbol: :param exchange: :param start_date: @@ -494,6 +509,9 @@ cdef class TradingPair(Asset): :param auto_close_date: :param exchange_full: :param min_trade_size: + :param maker: + :param taker: + :param data_source """ symbol = symbol.lower() @@ -527,13 +545,17 @@ cdef class TradingPair(Asset): first_traded=first_traded, auto_close_date=auto_close_date, exchange_full=exchange_full, - min_trade_size=min_trade_size + min_trade_size=min_trade_size, ) + self.maker = maker + self.taker = taker self.leverage = leverage self.end_daily = end_daily self.end_minute = end_minute self.exchange_symbol = exchange_symbol + self.trading_state = trading_state + self.data_source = data_source def __repr__(self): return 'Trading Pair {symbol}({sid}) Exchange: {exchange}, ' \ @@ -579,7 +601,7 @@ cdef class TradingPair(Asset): boolean: whether the asset's exchange is open at the given minute. """ #TODO: consider implementing to spot holds - return True + return self.trading_state > 0 cpdef __reduce__(self): """ diff --git a/catalyst/examples/mean_reversion_simple.py b/catalyst/examples/mean_reversion_simple.py index 6d4922e1..1927f746 100644 --- a/catalyst/examples/mean_reversion_simple.py +++ b/catalyst/examples/mean_reversion_simple.py @@ -36,8 +36,8 @@ def initialize(context): context.base_price = None context.current_day = None - context.RSI_OVERSOLD = 50 - context.RSI_OVERBOUGHT = 80 + context.RSI_OVERSOLD = 25 + context.RSI_OVERBOUGHT = 82 context.CANDLE_SIZE = '5T' context.start_time = time.time() @@ -247,14 +247,14 @@ if __name__ == '__main__': out = os.path.join(folder, '{}.p'.format(timestr)) # catalyst run -f catalyst/examples/mean_reversion_simple.py -x poloniex -s 2017-10-1 -e 2017-11-10 -c usdt -n mean-reversion --data-frequency minute --capital-base 10000 run_algorithm( - capital_base=10000, + capital_base=0.5, data_frequency='minute', initialize=initialize, handle_data=handle_data, analyze=analyze, exchange_name='bitfinex', algo_namespace=NAMESPACE, - base_currency='usd', + base_currency='eth', start=pd.to_datetime('2017-10-01', utc=True), end=pd.to_datetime('2017-11-10', utc=True), output=out diff --git a/catalyst/exchange/ccxt/ccxt_exchange.py b/catalyst/exchange/ccxt/ccxt_exchange.py index 86673a1c..00dd2970 100644 --- a/catalyst/exchange/ccxt/ccxt_exchange.py +++ b/catalyst/exchange/ccxt/ccxt_exchange.py @@ -3,12 +3,15 @@ from collections import defaultdict import ccxt import pandas as pd +from catalyst.assets._assets import TradingPair 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 +from catalyst.exchange.exchange_errors import InvalidHistoryFrequencyError, \ + ExchangeSymbolsNotFound +from catalyst.exchange.exchange_utils import mixin_market_params log = Logger('CCXT', level=LOG_LEVEL) @@ -96,6 +99,57 @@ class CCXT(Exchange): )) return candles + def load_assets(self, is_local=False): + markets = self.api.fetch_markets() + try: + symbol_map = self.fetch_symbol_map(is_local) + except ExchangeSymbolsNotFound: + return None + + data_source = 'local' if is_local else 'catalyst' + for market in markets: + asset = symbol_map[market['id']] \ + if market['id'] in markets else None + + params = dict(exchange=self.name, data_source=data_source) + mixin_market_params(params, market) + + if asset: + params['symbol'] = asset['symbol'] + + params['start_date'] = pd.to_datetime( + asset['start_date'], utc=True + ) if 'start_date' in asset else None + + params['end_date'] = pd.to_datetime( + asset['end_date'], utc=True + ) if 'end_date' in asset else None + + params['leverage'] = asset['leverage'] \ + if 'leverage' in asset else 1.0 + + params['asset_name'] = asset['asset_name'] \ + if 'asset_name' in asset else None + + params['end_daily'] = pd.to_datetime( + asset['end_daily'], utc=True + ) if 'end_daily' in asset and asset['end_daily'] != 'N/A' \ + else None + + params['end_minute'] = pd.to_datetime( + asset['end_minute'], utc=True + ) if 'end_minute' in asset and asset['end_minute'] != 'N/A' \ + else None + + else: + params['symbol'] = market['id'] + + trading_pair = TradingPair(**params) + if is_local: + self.local_assets[market['id']] = trading_pair + else: + self.assets[market['id']] = trading_pair + def get_balances(self): return None diff --git a/catalyst/exchange/exchange_utils.py b/catalyst/exchange/exchange_utils.py index 678d9945..83dfae41 100644 --- a/catalyst/exchange/exchange_utils.py +++ b/catalyst/exchange/exchange_utils.py @@ -571,3 +571,22 @@ def resample_history_df(df, freq, field): resampled_df = df.resample(freq).agg(agg) return resampled_df + + +def mixin_market_params(params, market): + """ + Applies a CCXT market dict to parameters of TradingPair init. + + Parameters + ---------- + params: dict[Object] + market: dict[Object] + + Returns + ------- + + """ + params['min_trade_size'] = market['lot'] + params['maker'] = market['maker'] + params['taker'] = market['taker'] + params['trading_state'] = 1 if int(market['info']['isFrozen']) == 0 else 0 diff --git a/tests/exchange/test_ccxt.py b/tests/exchange/test_ccxt.py index 7fd0dcff..f920fa39 100644 --- a/tests/exchange/test_ccxt.py +++ b/tests/exchange/test_ccxt.py @@ -99,3 +99,6 @@ class TestCCXT(BaseExchangeTestCase): asset = self.exchange.get_asset('eth_btc') orderbook = self.exchange.get_orderbook(asset) pass + + def test_get_fees(self): + pass