From d423f5856679cd7e2c4e61a56e98b1629daeb6f1 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Mon, 4 Mar 2019 21:21:32 +0100 Subject: [PATCH 01/24] replace fetch_markets --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 32d952542..1fcf20ff8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -657,7 +657,7 @@ class Exchange(object): @retrier def get_markets(self) -> List[dict]: try: - return self._api.fetch_markets() + return list(self.markets.values()) except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( f'Could not load markets due to {e.__class__.__name__}. Message: {e}') From 3a2aa54d2afb4e3498624826ba6d33d77f7c5e89 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Mon, 4 Mar 2019 23:59:08 +0100 Subject: [PATCH 02/24] add markets property --- freqtrade/exchange/exchange.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1fcf20ff8..b199f6051 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -106,7 +106,7 @@ class Exchange(object): logger.info('Using Exchange "%s"', self.name) - self.markets = self._load_markets() + self._load_markets() # Check if all pairs are available self.validate_pairs(config['exchange']['pair_whitelist']) self.validate_ordertypes(config.get('order_types', {})) @@ -165,6 +165,11 @@ class Exchange(object): """exchange ccxt id""" return self._api.id + @property + def markets(self) -> Dict: + """exchange ccxt markets""" + return self._api.markets + def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame: if pair_interval in self._klines: return self._klines[pair_interval].copy() if copy else self._klines[pair_interval] From ccad883256d32080641b5eb5faade4a3e25ec27c Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 19:40:55 +0100 Subject: [PATCH 03/24] adjust get_markets --- freqtrade/exchange/exchange.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index b199f6051..71c1c9a08 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -661,13 +661,7 @@ class Exchange(object): @retrier def get_markets(self) -> List[dict]: - try: - return list(self.markets.values()) - except (ccxt.NetworkError, ccxt.ExchangeError) as e: - raise TemporaryError( - f'Could not load markets due to {e.__class__.__name__}. Message: {e}') - except ccxt.BaseError as e: - raise OperationalException(e) + return list(self.markets.values()) @retrier def get_fee(self, symbol='ETH/BTC', type='', side='', amount=1, From 47cc04c0a315b4ed05f3e8b42aff201d37f9f2fb Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 19:44:23 +0100 Subject: [PATCH 04/24] use self.markets instead of _api.markets --- freqtrade/exchange/exchange.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 71c1c9a08..a7e10d923 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -276,8 +276,8 @@ class Exchange(object): Returns the amount to buy or sell to a precision the Exchange accepts Rounded down ''' - if self._api.markets[pair]['precision']['amount']: - symbol_prec = self._api.markets[pair]['precision']['amount'] + if self.markets[pair]['precision']['amount']: + symbol_prec = self.markets[pair]['precision']['amount'] big_amount = amount * pow(10, symbol_prec) amount = floor(big_amount) / pow(10, symbol_prec) return amount @@ -287,8 +287,8 @@ class Exchange(object): Returns the price buying or selling with to the precision the Exchange accepts Rounds up ''' - if self._api.markets[pair]['precision']['price']: - symbol_prec = self._api.markets[pair]['precision']['price'] + if self.markets[pair]['precision']['price']: + symbol_prec = self.markets[pair]['precision']['price'] big_price = price * pow(10, symbol_prec) price = ceil(big_price) / pow(10, symbol_prec) return price From b24a22b0b6033ce8a3ca1df6ff599490afb37ee3 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 19:45:10 +0100 Subject: [PATCH 05/24] use self.markets instead of get_markets --- freqtrade/freqtradebot.py | 10 ++++------ freqtrade/pairlist/IPairList.py | 6 ++++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index dce3136df..939904c73 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -280,12 +280,10 @@ class FreqtradeBot(object): return stake_amount def _get_min_pair_stake_amount(self, pair: str, price: float) -> Optional[float]: - markets = self.exchange.get_markets() - markets = [m for m in markets if m['symbol'] == pair] - if not markets: - raise ValueError(f'Can\'t get market information for symbol {pair}') - - market = markets[0] + try: + market = self.exchange.markets[pair] + except KeyError: + raise ValueError(f"Can't get market information for symbol {pair}") if 'limits' not in market: return None diff --git a/freqtrade/pairlist/IPairList.py b/freqtrade/pairlist/IPairList.py index 948abe113..5559c582f 100644 --- a/freqtrade/pairlist/IPairList.py +++ b/freqtrade/pairlist/IPairList.py @@ -66,12 +66,14 @@ class IPairList(ABC): black_listed """ sanitized_whitelist = whitelist - markets = self._freqtrade.exchange.get_markets() + markets = self._freqtrade.exchange.markets # Filter to markets in stake currency - markets = [m for m in markets if m['quote'] == self._config['stake_currency']] + markets = [markets[pair] for pair in markets if + markets[pair]['quote'] == self._config['stake_currency']] known_pairs = set() + # TODO: we should loop over whitelist instead of all markets for market in markets: pair = market['symbol'] # pair is not in the generated dynamic market, or in the blacklist ... ignore it From 5c840f333f8c9795a1b45f9696f84f4caad0fe7d Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 19:45:42 +0100 Subject: [PATCH 06/24] slight change to exception message --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index a7e10d923..e98244656 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -226,7 +226,7 @@ class Exchange(object): f'Pair {pair} not compatible with stake_currency: {stake_cur}') if self.markets and pair not in self.markets: raise OperationalException( - f'Pair {pair} is not available at {self.name}' + f'Pair {pair} is not available on {self.name}. ' f'Please remove {pair} from your whitelist.') def validate_timeframes(self, timeframe: List[str]) -> None: From c30fb7f59088a3b2beb300d8dc0d4a9278c3cae0 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 19:45:54 +0100 Subject: [PATCH 07/24] return markets as dict --- freqtrade/tests/conftest.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 6237a27c9..4d363a173 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -37,10 +37,12 @@ def log_has_re(line, logs): def patch_exchange(mocker, api_mock=None, id='bittrex') -> None: mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id)) mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title())) + # mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})) if api_mock: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) @@ -225,8 +227,8 @@ def ticker_sell_down(): @pytest.fixture def markets(): - return MagicMock(return_value=[ - { + return { + 'ETH/BTC': { 'id': 'ethbtc', 'symbol': 'ETH/BTC', 'base': 'ETH', @@ -251,7 +253,7 @@ def markets(): }, 'info': '', }, - { + 'TKN/BTC': { 'id': 'tknbtc', 'symbol': 'TKN/BTC', 'base': 'TKN', @@ -276,7 +278,7 @@ def markets(): }, 'info': '', }, - { + 'BLK/BTC': { 'id': 'blkbtc', 'symbol': 'BLK/BTC', 'base': 'BLK', @@ -301,7 +303,7 @@ def markets(): }, 'info': '', }, - { + 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', @@ -326,7 +328,7 @@ def markets(): }, 'info': '', }, - { + 'XRP/BTC': { 'id': 'xrpbtc', 'symbol': 'XRP/BTC', 'base': 'XRP', @@ -351,7 +353,7 @@ def markets(): }, 'info': '', }, - { + 'NEO/BTC': { 'id': 'neobtc', 'symbol': 'NEO/BTC', 'base': 'NEO', @@ -376,7 +378,7 @@ def markets(): }, 'info': '', }, - { + 'BTT/BTC': { 'id': 'BTTBTC', 'symbol': 'BTT/BTC', 'base': 'BTT', @@ -404,7 +406,7 @@ def markets(): }, 'info': "", }, - { + 'ETH/USDT': { 'id': 'USDT-ETH', 'symbol': 'ETH/USDT', 'base': 'ETH', @@ -426,7 +428,7 @@ def markets(): 'active': True, 'info': "" }, - { + 'LTC/USDT': { 'id': 'USDT-LTC', 'symbol': 'LTC/USDT', 'base': 'LTC', @@ -448,7 +450,7 @@ def markets(): }, 'info': "" } - ]) + } @pytest.fixture From e234158cc90061c95889190ca6cfc35f54f152e8 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 19:46:03 +0100 Subject: [PATCH 08/24] update tests --- freqtrade/tests/exchange/test_exchange.py | 80 ++++---- freqtrade/tests/pairlist/test_pairlist.py | 14 +- freqtrade/tests/rpc/test_rpc.py | 20 +- freqtrade/tests/rpc/test_rpc_telegram.py | 24 ++- freqtrade/tests/test_freqtradebot.py | 224 ++++++++++------------ 5 files changed, 166 insertions(+), 196 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index ff36ab91c..0beea4ed3 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -152,10 +152,7 @@ def test_symbol_amount_prec(default_conf, mocker): markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'amount': 4}}}) type(api_mock).markets = markets - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) - exchange = Exchange(default_conf) + exchange = get_patched_exchange(mocker, default_conf, api_mock) amount = 2.34559 pair = 'ETH/BTC' @@ -176,10 +173,7 @@ def test_symbol_price_prec(default_conf, mocker): markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': 4}}}) type(api_mock).markets = markets - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) - exchange = Exchange(default_conf) + exchange = get_patched_exchange(mocker, default_conf, api_mock) price = 2.34559 pair = 'ETH/BTC' @@ -198,11 +192,7 @@ def test_set_sandbox(default_conf, mocker): url_mock = PropertyMock(return_value={'test': "api-public.sandbox.gdax.com", 'api': 'https://api.gdax.com'}) type(api_mock).urls = url_mock - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) - - exchange = Exchange(default_conf) + exchange = get_patched_exchange(mocker, default_conf, api_mock) liveurl = exchange._api.urls['api'] default_conf['exchange']['sandbox'] = True exchange.set_sandbox(exchange._api, default_conf['exchange'], 'Logname') @@ -220,12 +210,8 @@ def test_set_sandbox_exception(default_conf, mocker): url_mock = PropertyMock(return_value={'api': 'https://api.gdax.com'}) type(api_mock).urls = url_mock - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) - with pytest.raises(OperationalException, match=r'does not provide a sandbox api'): - exchange = Exchange(default_conf) + exchange = get_patched_exchange(mocker, default_conf, api_mock) default_conf['exchange']['sandbox'] = True exchange.set_sandbox(exchange._api, default_conf['exchange'], 'Logname') @@ -247,29 +233,27 @@ def test__load_async_markets(default_conf, mocker, caplog): def test__load_markets(default_conf, mocker, caplog): caplog.set_level(logging.INFO) api_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance')) - - api_mock.load_markets = MagicMock(return_value={}) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) - - expected_return = {'ETH/BTC': 'available'} - api_mock.load_markets = MagicMock(return_value=expected_return) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - default_conf['exchange']['pair_whitelist'] = ['ETH/BTC'] - ex = Exchange(default_conf) - assert ex.markets == expected_return - api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError()) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) Exchange(default_conf) assert log_has('Unable to initialize markets. Reason: ', caplog.record_tuples) - -def test_validate_pairs(default_conf, mocker): + expected_return = {'ETH/BTC': 'available'} api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={ + api_mock.load_markets = MagicMock(return_value=expected_return) + type(api_mock).markets = expected_return + mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) + default_conf['exchange']['pair_whitelist'] = ['ETH/BTC'] + ex = get_patched_exchange(mocker, default_conf, api_mock, id="binance") + assert ex.markets == expected_return + + +def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly + api_mock = MagicMock() + type(api_mock).markets = PropertyMock(return_value={ 'ETH/BTC': '', 'LTC/BTC': '', 'XRP/BTC': '', 'NEO/BTC': '' }) id_mock = PropertyMock(return_value='test_exchange') @@ -283,7 +267,9 @@ def test_validate_pairs(default_conf, mocker): def test_validate_pairs_not_available(default_conf, mocker): api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={'XRP/BTC': 'inactive'}) + type(api_mock).markets = PropertyMock(return_value={ + 'XRP/BTC': 'inactive' + }) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) @@ -294,7 +280,7 @@ def test_validate_pairs_not_available(default_conf, mocker): def test_validate_pairs_not_compatible(default_conf, mocker): api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={ + type(api_mock).markets = PropertyMock(return_value={ 'ETH/BTC': '', 'TKN/BTC': '', 'TRST/BTC': '', 'SWT/BTC': '', 'BCC/BTC': '' }) default_conf['stake_currency'] = 'ETH' @@ -310,15 +296,15 @@ def test_validate_pairs_exception(default_conf, mocker, caplog): api_mock = MagicMock() mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance')) - api_mock.load_markets = MagicMock(return_value={}) + type(api_mock).markets = PropertyMock(return_value={}) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) - with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at Binance'): + with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available on Binance'): Exchange(default_conf) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})) Exchange(default_conf) assert log_has('Unable to validate pairs (assuming they are correct).', caplog.record_tuples) @@ -353,6 +339,7 @@ def test_validate_timeframes(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) Exchange(default_conf) @@ -369,6 +356,7 @@ def test_validate_timeframes_failed(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) with pytest.raises(OperationalException, match=r'Invalid ticker 3m, this Exchange supports.*'): Exchange(default_conf) @@ -386,6 +374,7 @@ def test_validate_timeframes_not_in_config(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) Exchange(default_conf) @@ -395,6 +384,7 @@ def test_validate_order_types(default_conf, mocker): type(api_mock).has = PropertyMock(return_value={'createMarketOrder': True}) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex') default_conf['order_types'] = { @@ -436,6 +426,7 @@ def test_validate_order_types_not_in_config(default_conf, mocker): api_mock = MagicMock() mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) conf = copy.deepcopy(default_conf) @@ -1314,9 +1305,9 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_get_markets(default_conf, mocker, markets, exchange_name): - api_mock = MagicMock() - api_mock.fetch_markets = markets - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) + exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) ret = exchange.get_markets() assert isinstance(ret, list) assert len(ret) == 9 @@ -1324,9 +1315,6 @@ def test_get_markets(default_conf, mocker, markets, exchange_name): assert ret[0]["id"] == "ethbtc" assert ret[0]["symbol"] == "ETH/BTC" - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - 'get_markets', 'fetch_markets') - @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_get_fee(default_conf, mocker, exchange_name): diff --git a/freqtrade/tests/pairlist/test_pairlist.py b/freqtrade/tests/pairlist/test_pairlist.py index dd6ebb62c..c40e16f77 100644 --- a/freqtrade/tests/pairlist/test_pairlist.py +++ b/freqtrade/tests/pairlist/test_pairlist.py @@ -1,6 +1,6 @@ # pragma pylint: disable=missing-docstring,C0103,protected-access -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock from freqtrade import OperationalException from freqtrade.constants import AVAILABLE_PAIRLISTS @@ -44,7 +44,7 @@ def test_refresh_market_pair_not_in_whitelist(mocker, markets, whitelist_conf): freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf) - mocker.patch('freqtrade.exchange.Exchange.get_markets', markets) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) freqtradebot.pairlists.refresh_pairlist() # List ordered by BaseVolume whitelist = ['ETH/BTC', 'TKN/BTC'] @@ -58,7 +58,7 @@ def test_refresh_market_pair_not_in_whitelist(mocker, markets, whitelist_conf): def test_refresh_pairlists(mocker, markets, whitelist_conf): freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf) - mocker.patch('freqtrade.exchange.Exchange.get_markets', markets) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) freqtradebot.pairlists.refresh_pairlist() # List ordered by BaseVolume whitelist = ['ETH/BTC', 'TKN/BTC'] @@ -73,7 +73,7 @@ def test_refresh_pairlist_dynamic(mocker, markets, tickers, whitelist_conf): } mocker.patch.multiple( 'freqtrade.exchange.Exchange', - get_markets=markets, + markets=PropertyMock(return_value=markets), get_tickers=tickers, exchange_has=MagicMock(return_value=True) ) @@ -96,7 +96,7 @@ def test_refresh_pairlist_dynamic(mocker, markets, tickers, whitelist_conf): def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf) - mocker.patch('freqtrade.exchange.Exchange.get_markets', markets_empty) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets_empty)) # argument: use the whitelist dynamically by exchange-volume whitelist = [] @@ -111,7 +111,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, markets, tickers) whitelist_conf['pairlist']['method'] = 'VolumePairList' mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) - mocker.patch('freqtrade.exchange.Exchange.get_markets', markets) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers) mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, p, r: round(r, 8)) @@ -157,7 +157,7 @@ def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None @pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS) def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): whitelist_conf['pairlist']['method'] = pairlist - mocker.patch('freqtrade.exchange.Exchange.get_markets', markets) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 2de2668e8..e1261e02e 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -2,7 +2,7 @@ # pragma pylint: disable=invalid-sequence-index, invalid-name, too-many-arguments from datetime import datetime -from unittest.mock import MagicMock, ANY +from unittest.mock import MagicMock, ANY, PropertyMock import pytest from numpy import isnan @@ -34,7 +34,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -90,7 +90,7 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -126,7 +126,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -180,7 +180,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -268,7 +268,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -424,7 +424,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: } ), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -516,7 +516,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -552,7 +552,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtradebot = FreqtradeBot(default_conf) @@ -581,7 +581,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=buy_mm ) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 9964973e1..b5055d7f5 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -5,7 +5,7 @@ import re from datetime import datetime from random import randint -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock import arrow import pytest @@ -184,7 +184,7 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(markets) ) msg_mock = MagicMock() status_table = MagicMock() @@ -693,7 +693,8 @@ def test_forcesell_handle(default_conf, update, ticker, fee, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets), + validate_pairs=MagicMock(return_value={}) ) freqtradebot = FreqtradeBot(default_conf) @@ -743,7 +744,8 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets), + validate_pairs=MagicMock(return_value={}) ) freqtradebot = FreqtradeBot(default_conf) @@ -796,7 +798,8 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets), + validate_pairs=MagicMock(return_value={}) ) freqtradebot = FreqtradeBot(default_conf) @@ -878,7 +881,8 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', _load_markets=MagicMock(return_value={}), - get_markets=markets + markets=PropertyMock(markets), + validate_pairs=MagicMock(return_value={}) ) fbuy_mock = MagicMock(return_value=None) mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) @@ -914,7 +918,8 @@ def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> Non mocker.patch.multiple( 'freqtrade.exchange.Exchange', _load_markets=MagicMock(return_value={}), - get_markets=markets + markets=PropertyMock(markets), + validate_pairs=MagicMock(return_value={}) ) freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) @@ -941,7 +946,8 @@ def test_performance_handle(default_conf, update, ticker, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(markets), + validate_pairs=MagicMock(return_value={}) ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) freqtradebot = FreqtradeBot(default_conf) @@ -980,7 +986,7 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non 'freqtrade.exchange.Exchange', get_ticker=ticker, buy=MagicMock(return_value={'id': 'mocked_order_id'}), - get_markets=markets + markets=PropertyMock(markets) ) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) freqtradebot = FreqtradeBot(default_conf) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 2f66a5153..178081aff 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -5,7 +5,7 @@ import logging import re import time from copy import deepcopy -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock import arrow import pytest @@ -59,7 +59,8 @@ def patch_RPCManager(mocker) -> MagicMock: # Unit tests -def test_freqtradebot(mocker, default_conf) -> None: +def test_freqtradebot(mocker, default_conf, markets) -> None: + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) freqtrade = get_patched_freqtradebot(mocker, default_conf) assert freqtrade.state is State.RUNNING @@ -71,7 +72,6 @@ def test_freqtradebot(mocker, default_conf) -> None: def test_cleanup(mocker, default_conf, caplog) -> None: mock_cleanup = MagicMock() mocker.patch('freqtrade.persistence.cleanup', mock_cleanup) - freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade.cleanup() assert log_has('Cleaning up modules ...', caplog.record_tuples) @@ -171,11 +171,10 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, patch_wallet(mocker, free=default_conf['stake_amount']) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - validate_pairs=MagicMock(), + markets=PropertyMock(return_value=markets), get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), - get_fee=fee, - get_markets=markets + get_fee=fee ) conf = deepcopy(default_conf) @@ -253,7 +252,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, markets, caplog, mocker, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets) ) ############################################# @@ -293,7 +292,7 @@ def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, markets, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), ) ############################################# @@ -321,7 +320,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -349,131 +348,108 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None: patch_exchange(mocker) freqtrade = FreqtradeBot(default_conf) freqtrade.strategy.stoploss = -0.05 + markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}} # no pair found mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC' - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) with pytest.raises(ValueError, match=r'.*get market information.*'): freqtrade._get_min_pair_stake_amount('BNB/BTC', 1) # no 'limits' section - mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC' - }]) - ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) assert result is None # empty 'limits' section + markets["ETH/BTC"]["limits"] = {} mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': {} - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) assert result is None # no cost Min + markets["ETH/BTC"]["limits"] = { + 'cost': {"min": None}, + 'amount': {} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {"min": None}, - 'amount': {} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) assert result is None # no amount Min + markets["ETH/BTC"]["limits"] = { + 'cost': {}, + 'amount': {"min": None} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {}, - 'amount': {"min": None} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) assert result is None # empty 'cost'/'amount' section + markets["ETH/BTC"]["limits"] = { + 'cost': {}, + 'amount': {} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {}, - 'amount': {} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) assert result is None # min cost is set + markets["ETH/BTC"]["limits"] = { + 'cost': {'min': 2}, + 'amount': {} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {'min': 2}, - 'amount': {} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 1) assert result == 2 / 0.9 # min amount is set + markets["ETH/BTC"]["limits"] = { + 'cost': {}, + 'amount': {'min': 2} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {}, - 'amount': {'min': 2} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) assert result == 2 * 2 / 0.9 # min amount and cost are set (cost is minimal) + markets["ETH/BTC"]["limits"] = { + 'cost': {'min': 2}, + 'amount': {'min': 2} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {'min': 2}, - 'amount': {'min': 2} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) assert result == min(2, 2 * 2) / 0.9 # min amount and cost are set (amount is minial) + markets["ETH/BTC"]["limits"] = { + 'cost': {'min': 8}, + 'amount': {'min': 2} + } mocker.patch( - 'freqtrade.exchange.Exchange.get_markets', - MagicMock(return_value=[{ - 'symbol': 'ETH/BTC', - 'limits': { - 'cost': {'min': 8}, - 'amount': {'min': 2} - } - }]) + 'freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=markets) ) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) assert result == min(8, 2 * 2) / 0.9 @@ -487,7 +463,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, markets, mocke get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) # Save state of current whitelist @@ -522,7 +498,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -541,7 +517,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, get_ticker=ticker, buy=buy_mock, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['stake_amount'] = 0.0005 freqtrade = FreqtradeBot(default_conf) @@ -562,7 +538,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord get_ticker=ticker, buy=buy_mock, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['stake_amount'] = 0.000000005 @@ -583,7 +559,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_balance=MagicMock(return_value=default_conf['stake_amount']), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['max_open_trades'] = 0 default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT @@ -603,7 +579,7 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, marke get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['exchange']['pair_whitelist'] = ["ETH/BTC"] @@ -626,7 +602,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['exchange']['pair_whitelist'] = ["ETH/BTC"] default_conf['exchange']['pair_blacklist'] = ["ETH/BTC"] @@ -665,7 +641,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_order=MagicMock(return_value=limit_buy_order), get_fee=fee, @@ -702,7 +678,7 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=MagicMock(side_effect=TemporaryError) ) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) @@ -721,7 +697,7 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) -> mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=MagicMock(side_effect=OperationalException) ) freqtrade = FreqtradeBot(default_conf) @@ -742,7 +718,7 @@ def test_process_trade_handling( mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_order=MagicMock(return_value=limit_buy_order), get_fee=fee, @@ -769,7 +745,7 @@ def test_process_trade_no_whitelist_pair( mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_order=MagicMock(return_value=limit_buy_order), get_fee=fee, @@ -818,7 +794,7 @@ def test_process_informative_pairs_added(default_conf, ticker, markets, mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, - get_markets=markets, + markets=PropertyMock(return_value=markets), buy=MagicMock(side_effect=TemporaryError), refresh_latest_ohlcv=refresh_mock, ) @@ -886,7 +862,7 @@ def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> Non }), buy=buy_mm, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) pair = 'ETH/BTC' print(buy_mm.call_args_list) @@ -997,7 +973,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), stoploss_limit=stoploss_limit ) freqtrade = FreqtradeBot(default_conf) @@ -1066,7 +1042,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), stoploss_limit=stoploss_limit ) @@ -1164,7 +1140,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), stoploss_limit=stoploss_limit ) @@ -1348,7 +1324,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -1386,7 +1362,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) @@ -1442,7 +1418,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) @@ -1475,7 +1451,7 @@ def test_handle_trade_experimental( get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) @@ -1503,7 +1479,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -1846,7 +1822,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -1891,7 +1867,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -1939,7 +1915,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -1996,7 +1972,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) stoploss_limit = MagicMock(return_value={ @@ -2051,7 +2027,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) stoploss_limit = MagicMock(return_value={ @@ -2116,7 +2092,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2162,7 +2138,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee, _load_markets=MagicMock(return_value={}), get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2213,7 +2189,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['experimental'] = { 'use_sell_signal': True, @@ -2245,7 +2221,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['experimental'] = { 'use_sell_signal': True, @@ -2275,7 +2251,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['experimental'] = { 'use_sell_signal': True, @@ -2306,7 +2282,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['experimental'] = { 'use_sell_signal': True, @@ -2338,7 +2314,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['experimental'] = { 'ignore_roi_if_buy_signal': True @@ -2372,7 +2348,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), ) default_conf['trailing_stop'] = True freqtrade = FreqtradeBot(default_conf) @@ -2407,7 +2383,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), ) default_conf['trailing_stop'] = True default_conf['trailing_stop_positive'] = 0.01 @@ -2465,7 +2441,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets, + markets=PropertyMock(return_value=markets), ) default_conf['trailing_stop'] = True @@ -2525,7 +2501,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) default_conf['experimental'] = { 'ignore_roi_if_buy_signal': False @@ -2760,7 +2736,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order, fee, get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) # Save state of current whitelist @@ -2796,7 +2772,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) # Save state of current whitelist freqtrade = FreqtradeBot(default_conf) @@ -2816,7 +2792,7 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, markets) ticker_mock = MagicMock(return_value={'ask': 0.045, 'last': 0.046}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - get_markets=markets, + markets=PropertyMock(return_value=markets), get_order_book=order_book_l2, get_ticker=ticker_mock, @@ -2841,7 +2817,7 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) ticker_mock = MagicMock(return_value={'ask': 0.042, 'last': 0.046}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - get_markets=markets, + markets=PropertyMock(return_value=markets), get_order_book=order_book_l2, get_ticker=ticker_mock, @@ -2865,7 +2841,7 @@ def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2, markets) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - get_markets=markets, + markets=PropertyMock(return_value=markets), get_order_book=order_book_l2 ) default_conf['telegram']['enabled'] = False @@ -2902,7 +2878,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order buy=MagicMock(return_value={'id': limit_buy_order['id']}), sell=MagicMock(return_value={'id': limit_sell_order['id']}), get_fee=fee, - get_markets=markets + markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) From 6b97af4a03bd1f588a7a64cb57696d336f31d0e6 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 5 Mar 2019 20:18:35 +0100 Subject: [PATCH 09/24] add comment --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e98244656..7e97923b0 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -200,7 +200,7 @@ class Exchange(object): try: markets = self._api.load_markets() self._load_async_markets() - return markets + return markets # prbly not necessary to return anything anymore except ccxt.BaseError as e: logger.warning('Unable to initialize markets. Reason: %s', e) return {} From 041e9957dd312fdefb901e4265afd5ac40068256 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Wed, 6 Mar 2019 22:48:04 +0100 Subject: [PATCH 10/24] add reload argument --- freqtrade/exchange/exchange.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7e97923b0..994e1c3a4 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -186,24 +186,23 @@ class Exchange(object): "Please check your config.json") raise OperationalException(f'Exchange {name} does not provide a sandbox api') - def _load_async_markets(self) -> None: + def _load_async_markets(self, reload=False) -> None: try: if self._api_async: - asyncio.get_event_loop().run_until_complete(self._api_async.load_markets()) + asyncio.get_event_loop().run_until_complete( + self._api_async.load_markets(reload=reload)) except ccxt.BaseError as e: logger.warning('Could not load async markets. Reason: %s', e) return - def _load_markets(self) -> Dict[str, Any]: + def _load_markets(self, reload=False) -> Dict[str, Any]: """ Initialize markets both sync and async """ try: - markets = self._api.load_markets() - self._load_async_markets() - return markets # prbly not necessary to return anything anymore + self._api.load_markets(reload=reload) + self._load_async_markets(reload=reload) except ccxt.BaseError as e: logger.warning('Unable to initialize markets. Reason: %s', e) - return {} def validate_pairs(self, pairs: List[str]) -> None: """ From df9410cd1569957cff7edcec3bbe458eabbad4fd Mon Sep 17 00:00:00 2001 From: iuvbio Date: Wed, 6 Mar 2019 22:57:31 +0100 Subject: [PATCH 11/24] check if markets were loaded --- freqtrade/exchange/exchange.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 994e1c3a4..2d811e8e8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -168,6 +168,9 @@ class Exchange(object): @property def markets(self) -> Dict: """exchange ccxt markets""" + if not self._api.markets: + logger.warning("Markets were not loaded. Loading them now..") + self._load_markets() return self._api.markets def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame: From 3ad0686bc78018ccc6c19cfa6ffad72d47e62bed Mon Sep 17 00:00:00 2001 From: iuvbio Date: Wed, 6 Mar 2019 23:00:28 +0100 Subject: [PATCH 12/24] fix typing --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 2d811e8e8..8eb74bdd5 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -199,7 +199,7 @@ class Exchange(object): logger.warning('Could not load async markets. Reason: %s', e) return - def _load_markets(self, reload=False) -> Dict[str, Any]: + def _load_markets(self, reload=False) -> None: """ Initialize markets both sync and async """ try: self._api.load_markets(reload=reload) From 0d980134e7f946aaae9894c0611657a54a8c0b2f Mon Sep 17 00:00:00 2001 From: iuvbio Date: Sun, 10 Mar 2019 13:30:45 +0100 Subject: [PATCH 13/24] add markets reload func --- freqtrade/exchange/exchange.py | 19 +++++++++++++++++++ freqtrade/freqtradebot.py | 3 +++ 2 files changed, 22 insertions(+) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 8eb74bdd5..30bf9a30c 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -88,6 +88,8 @@ class Exchange(object): # Holds last candle refreshed time of each pair self._pairs_last_refresh_time: Dict[Tuple[str, str], int] = {} + # Timestamp of last markets refresh + self._last_markets_refresh: int = 0 # Holds candles self._klines: Dict[Tuple[str, str], DataFrame] = {} @@ -106,7 +108,12 @@ class Exchange(object): logger.info('Using Exchange "%s"', self.name) + # Converts the interval provided in minutes in config to seconds + self.markets_refresh_interval: int = exchange_config.get( + "markets_refresh_interval", 60) * 60 + # Initial markets load self._load_markets() + # Check if all pairs are available self.validate_pairs(config['exchange']['pair_whitelist']) self.validate_ordertypes(config.get('order_types', {})) @@ -204,9 +211,21 @@ class Exchange(object): try: self._api.load_markets(reload=reload) self._load_async_markets(reload=reload) + self._last_markets_refresh = arrow.utcnow().timestamp except ccxt.BaseError as e: logger.warning('Unable to initialize markets. Reason: %s', e) + def _reload_markets(self) -> None: + """Reload markets both sync and async, if refresh interval has passed""" + # Check whether markets have to be reloaded + if (self._last_markets_refresh > 0) and ( + self._last_markets_refresh + self.markets_refresh_interval + > arrow.utcnow().timestamp): + return None + else: + logger.debug("Performing scheduled market reload..") + self._load_markets(reload=True) + def validate_pairs(self, pairs: List[str]) -> None: """ Checks if all given pairs are tradable on the current exchange. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 939904c73..0422b0f88 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -155,6 +155,9 @@ class FreqtradeBot(object): """ state_changed = False try: + # Check whether markets have to be reloaded + self.exchange._reload_markets() + # Refresh whitelist self.pairlists.refresh_pairlist() self.active_pair_whitelist = self.pairlists.whitelist From 35c2b961be3899b1220e581143f051b00c2f03d8 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Sun, 10 Mar 2019 13:30:52 +0100 Subject: [PATCH 14/24] add config param --- config_full.json.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config_full.json.example b/config_full.json.example index 0f46a62e3..a1aabb1b8 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -77,7 +77,8 @@ "pair_blacklist": [ "DOGE/BTC" ], - "outdated_offset": 5 + "outdated_offset": 5, + "markets_refresh_interval": 60 }, "edge": { "enabled": false, From 87410178199ad60a52e2969506b093ca99beda4a Mon Sep 17 00:00:00 2001 From: iuvbio Date: Sun, 10 Mar 2019 15:26:55 +0100 Subject: [PATCH 15/24] remove get_markets --- freqtrade/exchange/exchange.py | 4 ---- freqtrade/tests/exchange/test_exchange.py | 13 ------------- freqtrade/tests/pairlist/test_pairlist.py | 2 +- freqtrade/tests/rpc/test_rpc_telegram.py | 8 ++++---- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 30bf9a30c..272d81e59 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -680,10 +680,6 @@ class Exchange(object): except ccxt.BaseError as e: raise OperationalException(e) - @retrier - def get_markets(self) -> List[dict]: - return list(self.markets.values()) - @retrier def get_fee(self, symbol='ETH/BTC', type='', side='', amount=1, price=1, taker_or_maker='maker') -> float: diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 0beea4ed3..e9debbfc2 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1303,19 +1303,6 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name): assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == [] -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_markets(default_conf, mocker, markets, exchange_name): - mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - ret = exchange.get_markets() - assert isinstance(ret, list) - assert len(ret) == 9 - - assert ret[0]["id"] == "ethbtc" - assert ret[0]["symbol"] == "ETH/BTC" - - @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_get_fee(default_conf, mocker, exchange_name): api_mock = MagicMock() diff --git a/freqtrade/tests/pairlist/test_pairlist.py b/freqtrade/tests/pairlist/test_pairlist.py index c40e16f77..52f44c41b 100644 --- a/freqtrade/tests/pairlist/test_pairlist.py +++ b/freqtrade/tests/pairlist/test_pairlist.py @@ -33,7 +33,7 @@ def whitelist_conf(default_conf): def test_load_pairlist_noexist(mocker, markets, default_conf): freqtradebot = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.get_markets', markets) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) with pytest.raises(ImportError, match=r"Impossible to load Pairlist 'NonexistingPairList'." r" This class does not exist or contains Python code errors"): diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index b5055d7f5..02f4f4afb 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -232,7 +232,7 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(markets) ) msg_mock = MagicMock() status_table = MagicMock() @@ -279,7 +279,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) get_ticker=ticker, buy=MagicMock(return_value={'id': 'mocked_order_id'}), get_fee=fee, - get_markets=markets + markets=PropertyMock(markets) ) msg_mock = MagicMock() mocker.patch.multiple( @@ -334,7 +334,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(markets) ) msg_mock = MagicMock() mocker.patch.multiple( @@ -438,7 +438,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - get_markets=markets + markets=PropertyMock(markets) ) msg_mock = MagicMock() mocker.patch.multiple( From 1a92bf9e8ec0c3bd0db39ed0b6a20d52417840c0 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Sun, 10 Mar 2019 16:36:25 +0100 Subject: [PATCH 16/24] add test --- freqtrade/exchange/exchange.py | 5 ++-- freqtrade/tests/exchange/test_exchange.py | 28 ++++++++++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 272d81e59..f177e1d54 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -222,9 +222,8 @@ class Exchange(object): self._last_markets_refresh + self.markets_refresh_interval > arrow.utcnow().timestamp): return None - else: - logger.debug("Performing scheduled market reload..") - self._load_markets(reload=True) + logger.debug("Performing scheduled market reload..") + self._load_markets(reload=True) def validate_pairs(self, pairs: List[str]) -> None: """ diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e9debbfc2..08e460336 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -245,12 +245,38 @@ def test__load_markets(default_conf, mocker, caplog): api_mock = MagicMock() api_mock.load_markets = MagicMock(return_value=expected_return) type(api_mock).markets = expected_return - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) default_conf['exchange']['pair_whitelist'] = ['ETH/BTC'] ex = get_patched_exchange(mocker, default_conf, api_mock, id="binance") assert ex.markets == expected_return +def test__reload_markets(default_conf, mocker, caplog): + caplog.set_level(logging.DEBUG) + api_mock = MagicMock() + initial_markets = {'ETH/BTC': {}} + type(api_mock).markets = initial_markets + updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} + default_conf['exchange']['markets_refresh_interval'] = 10 + exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") + exchange._last_markets_refresh = arrow.utcnow().timestamp + + def _load_markets(*args, **kwargs): + exchange._api.markets = updated_markets + + mocker.patch('freqtrade.exchange.Exchange._load_markets', _load_markets) + assert exchange.markets == initial_markets + + # less than 10 minutes have passed, no reload + exchange._reload_markets() + assert exchange.markets == initial_markets + + # more than 10 minutes have passed, reload is executed + exchange._last_markets_refresh = arrow.utcnow().timestamp - 15 * 60 + exchange._reload_markets() + assert exchange.markets == updated_markets + assert log_has('Performing scheduled market reload..', caplog.record_tuples) + + def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly api_mock = MagicMock() type(api_mock).markets = PropertyMock(return_value={ From deddbda26e1e5247dc8803c7ac338645f7903b92 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Sun, 10 Mar 2019 16:40:59 +0100 Subject: [PATCH 17/24] delete markets patch from conftest --- freqtrade/tests/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 4d363a173..26262cb4b 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -42,7 +42,6 @@ def patch_exchange(mocker, api_mock=None, id='bittrex') -> None: mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id)) mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title())) - # mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})) if api_mock: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) From 0ffefe44a7b73396e8209eadc29ee688e9f7e55e Mon Sep 17 00:00:00 2001 From: iuvbio Date: Sun, 10 Mar 2019 17:40:54 +0100 Subject: [PATCH 18/24] reorder vars --- freqtrade/tests/exchange/test_exchange.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 08e460336..41ca0a884 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -252,13 +252,13 @@ def test__load_markets(default_conf, mocker, caplog): def test__reload_markets(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) - api_mock = MagicMock() initial_markets = {'ETH/BTC': {}} + api_mock = MagicMock() type(api_mock).markets = initial_markets - updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} default_conf['exchange']['markets_refresh_interval'] = 10 exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") exchange._last_markets_refresh = arrow.utcnow().timestamp + updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} def _load_markets(*args, **kwargs): exchange._api.markets = updated_markets From 779bcdd990b098a64e4c9a67e2bd221653833228 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 12 Mar 2019 16:35:32 +0100 Subject: [PATCH 19/24] remove reload for async api --- freqtrade/exchange/exchange.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index f177e1d54..6ee5b17a2 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -206,11 +206,11 @@ class Exchange(object): logger.warning('Could not load async markets. Reason: %s', e) return - def _load_markets(self, reload=False) -> None: + def _load_markets(self) -> None: """ Initialize markets both sync and async """ try: - self._api.load_markets(reload=reload) - self._load_async_markets(reload=reload) + self._api.load_markets() + self._load_async_markets() self._last_markets_refresh = arrow.utcnow().timestamp except ccxt.BaseError as e: logger.warning('Unable to initialize markets. Reason: %s', e) @@ -223,7 +223,7 @@ class Exchange(object): > arrow.utcnow().timestamp): return None logger.debug("Performing scheduled market reload..") - self._load_markets(reload=True) + self._api.load_markets(reload=True) def validate_pairs(self, pairs: List[str]) -> None: """ From 299e64017095d9165c5c31d04e3b65a109e2ab45 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 12 Mar 2019 16:39:13 +0100 Subject: [PATCH 20/24] include markets_refresh_interval in docs --- docs/configuration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index d7e774595..10fe50a27 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -47,6 +47,7 @@ Mandatory Parameters are marked as **Required**. | `exchange.ccxt_rate_limit` | True | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. | `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `exchange.ccxt_async_config` | None | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| `exchange.markets_refresh_interval` | 60 | The interval in which markets are reloaded. | `edge` | false | Please refer to [edge configuration document](edge.md) for detailed explanation. | `experimental.use_sell_signal` | false | Use your sell strategy in addition of the `minimal_roi`. [Strategy Override](#parameters-in-strategy). | `experimental.sell_profit_only` | false | Waits until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-strategy). @@ -319,7 +320,7 @@ section of the configuration. * `VolumePairList` * Formerly available as `--dynamic-whitelist []`. This command line option is deprecated and should no longer be used. - * It selects `number_assets` top pairs based on `sort_key`, which can be one of + * It selects `number_assets` top pairs based on `sort_key`, which can be one of `askVolume`, `bidVolume` and `quoteVolume`, defaults to `quoteVolume`. * There is a possibility to filter low-value coins that would not allow setting a stop loss (set `precision_filter` parameter to `true` for this). From cb9849e192205cde0178d9b7e8df7a7a9bf948a9 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 12 Mar 2019 16:54:59 +0100 Subject: [PATCH 21/24] add markets_refresh_interval to CONF_SCHEMA --- freqtrade/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 4d0907d78..e3c059ae0 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -202,6 +202,7 @@ CONF_SCHEMA = { 'uniqueItems': True }, 'outdated_offset': {'type': 'integer', 'minimum': 1}, + 'markets_refresh_interval': {'type': 'integer'}, 'ccxt_config': {'type': 'object'}, 'ccxt_async_config': {'type': 'object'} }, From 7ffe65770e1b9db6c60ff5ef3e5a1ef6d666d78a Mon Sep 17 00:00:00 2001 From: iuvbio Date: Tue, 12 Mar 2019 17:54:16 +0100 Subject: [PATCH 22/24] fix test --- freqtrade/tests/exchange/test_exchange.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 41ca0a884..7c757df09 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -253,17 +253,18 @@ def test__load_markets(default_conf, mocker, caplog): def test__reload_markets(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) initial_markets = {'ETH/BTC': {}} + + def load_markets(*args, **kwargs): + exchange._api.markets = updated_markets + api_mock = MagicMock() + api_mock.load_markets = load_markets type(api_mock).markets = initial_markets default_conf['exchange']['markets_refresh_interval'] = 10 exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") exchange._last_markets_refresh = arrow.utcnow().timestamp updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} - def _load_markets(*args, **kwargs): - exchange._api.markets = updated_markets - - mocker.patch('freqtrade.exchange.Exchange._load_markets', _load_markets) assert exchange.markets == initial_markets # less than 10 minutes have passed, no reload From aa2d747d8fdac532d0fb3c6b6aa9a2cd500e470e Mon Sep 17 00:00:00 2001 From: iuvbio Date: Wed, 13 Mar 2019 20:08:51 +0100 Subject: [PATCH 23/24] update docs --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 10fe50a27..9b08f77c6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -47,7 +47,7 @@ Mandatory Parameters are marked as **Required**. | `exchange.ccxt_rate_limit` | True | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. | `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `exchange.ccxt_async_config` | None | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| `exchange.markets_refresh_interval` | 60 | The interval in which markets are reloaded. +| `exchange.markets_refresh_interval` | 60 | The interval in minutes in which markets are reloaded. | `edge` | false | Please refer to [edge configuration document](edge.md) for detailed explanation. | `experimental.use_sell_signal` | false | Use your sell strategy in addition of the `minimal_roi`. [Strategy Override](#parameters-in-strategy). | `experimental.sell_profit_only` | false | Waits until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-strategy). From a1841c35aec82775cb11078bc1e9e26a89785d7e Mon Sep 17 00:00:00 2001 From: iuvbio Date: Wed, 13 Mar 2019 20:18:49 +0100 Subject: [PATCH 24/24] reset _last_markets_refresh --- freqtrade/exchange/exchange.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 6ee5b17a2..c6b3a3796 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -224,6 +224,7 @@ class Exchange(object): return None logger.debug("Performing scheduled market reload..") self._api.load_markets(reload=True) + self._last_markets_refresh = arrow.utcnow().timestamp def validate_pairs(self, pairs: List[str]) -> None: """