From 4315c157c75527e408d30491ad270e5fdbbefe75 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Feb 2019 20:13:23 +0100 Subject: [PATCH 1/7] Move exception handling to resolver, add test --- freqtrade/freqtradebot.py | 8 +------- freqtrade/resolvers/exchange_resolver.py | 7 ++++++- freqtrade/tests/exchange/test_exchange.py | 20 +++++++++++++++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 656744ab6..92bdbc042 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -17,7 +17,6 @@ from freqtrade import (DependencyException, OperationalException, from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge -from freqtrade.exchange import Exchange from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver @@ -57,12 +56,7 @@ class FreqtradeBot(object): self.rpc: RPCManager = RPCManager(self) exchange_name = self.config.get('exchange', {}).get('name', 'bittrex').title() - try: - self.exchange = ExchangeResolver(exchange_name, self.config).exchange - except ImportError: - logger.info( - f"No {exchange_name} specific subclass found. Using the generic class instead.") - self.exchange = Exchange(self.config) + self.exchange = ExchangeResolver(exchange_name, self.config).exchange self.wallets = Wallets(self.exchange) self.dataprovider = DataProvider(self.config, self.exchange) diff --git a/freqtrade/resolvers/exchange_resolver.py b/freqtrade/resolvers/exchange_resolver.py index 62e89f445..a68219527 100644 --- a/freqtrade/resolvers/exchange_resolver.py +++ b/freqtrade/resolvers/exchange_resolver.py @@ -22,7 +22,12 @@ class ExchangeResolver(IResolver): Load the custom class from config parameter :param config: configuration dictionary or None """ - self.exchange = self._load_exchange(exchange_name, kwargs={'config': config}) + try: + self.exchange = self._load_exchange(exchange_name, kwargs={'config': config}) + except ImportError: + logger.info( + f"No {exchange_name} specific subclass found. Using the generic class instead.") + self.exchange = Exchange(config) def _load_exchange( self, exchange_name: str, kwargs: dict) -> Exchange: diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 6fb80194d..72919103c 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -13,7 +13,8 @@ from pandas import DataFrame from freqtrade import DependencyException, OperationalException, TemporaryError from freqtrade.exchange import API_RETRY_COUNT, Exchange -from freqtrade.tests.conftest import get_patched_exchange, log_has +from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re +from freqtrade.resolvers.exchange_resolver import ExchangeResolver # Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines @@ -106,6 +107,23 @@ def test_init_exception(default_conf, mocker): Exchange(default_conf) +def test_exchange_resolver(default_conf, mocker, caplog): + mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=MagicMock())) + mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) + exchange = ExchangeResolver('Binance', default_conf).exchange + assert isinstance(exchange, Exchange) + assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", + caplog.record_tuples) + caplog.clear() + + exchange = ExchangeResolver('Kraken', default_conf).exchange + assert isinstance(exchange, Exchange) + assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", + caplog.record_tuples) + + def test_symbol_amount_prec(default_conf, mocker): ''' Test rounds down to 4 Decimal places From af02e34b5720349858a98230855690089371565a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Feb 2019 21:04:52 +0100 Subject: [PATCH 2/7] Version bump to 0.18.1 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index ca148f518..0aa01211d 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ FreqTrade bot """ -__version__ = '0.18.1-dev' +__version__ = '0.18.1' class DependencyException(BaseException): From d9129cb9c5505f6263af2882b593ea94b1953af4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Feb 2019 21:07:54 +0100 Subject: [PATCH 3/7] Develop version bump to 0.18.2-dev --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 0aa01211d..0d1ae9c26 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ FreqTrade bot """ -__version__ = '0.18.1' +__version__ = '0.18.2-dev' class DependencyException(BaseException): From eb211706913b12fd5845ae5c606dc23adefbc0b4 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Thu, 21 Feb 2019 00:26:02 +0300 Subject: [PATCH 4/7] added amount_reserve_percent into config json-schema --- freqtrade/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 6c71ddf7b..ff250c1f1 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -67,6 +67,7 @@ CONF_SCHEMA = { }, 'minProperties': 1 }, + 'amount_reserve_percent': {'type': 'number', 'minimum': 0.0, 'maximum': 0.5}, 'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True}, 'trailing_stop': {'type': 'boolean'}, 'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1}, From 2aba9c081cd9a5a38de6f5e523a1bbcd322fca76 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 21 Feb 2019 00:46:35 +0300 Subject: [PATCH 5/7] fixed typos in comments --- freqtrade/exchange/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 47886989e..d05436a36 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -540,9 +540,9 @@ class Exchange(object): input_coroutines = [] - # Gather corotines to run + # Gather coroutines to run for pair, ticker_interval in set(pair_list): - # Calculating ticker interval in second + # Calculating ticker interval in seconds interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 if not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0) From c1ef6940b094958efc52ca5807fc39354caaff2c Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 21 Feb 2019 00:47:18 +0300 Subject: [PATCH 6/7] removed wrong comment: tuple is not created here --- freqtrade/exchange/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index d05436a36..c53858076 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -159,7 +159,6 @@ class Exchange(object): return self._api.id def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame: - # create key tuple if pair_interval in self._klines: return self._klines[pair_interval].copy() if copy else self._klines[pair_interval] else: From 285183372650e27490bb67dd5e519c487f23f48b Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 21 Feb 2019 01:20:24 +0300 Subject: [PATCH 7/7] added _now_is_time_to_refresh() --- freqtrade/exchange/__init__.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index c53858076..145e802fa 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -541,12 +541,8 @@ class Exchange(object): # Gather coroutines to run for pair, ticker_interval in set(pair_list): - # Calculating ticker interval in seconds - interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 - - if not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0) - + interval_in_sec) >= arrow.utcnow().timestamp - and (pair, ticker_interval) in self._klines): + if not ((pair, ticker_interval) in self._klines) \ + or self._now_is_time_to_refresh(pair, ticker_interval): input_coroutines.append(self._async_get_candle_history(pair, ticker_interval)) else: logger.debug("Using cached ohlcv data for %s, %s ...", pair, ticker_interval) @@ -570,6 +566,13 @@ class Exchange(object): ticks, tick_interval, fill_missing=True) return tickers + def _now_is_time_to_refresh(self, pair: str, ticker_interval: str) -> bool: + # Calculating ticker interval in seconds + interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 + + return not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0) + + interval_in_sec) >= arrow.utcnow().timestamp) + @retrier_async async def _async_get_candle_history(self, pair: str, tick_interval: str, since_ms: Optional[int] = None) -> Tuple[str, str, List]: