diff --git a/docs/configuration.md b/docs/configuration.md index 5e936065c..1ad13c87a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -75,8 +75,8 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** | `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** | `exchange.password` | '' | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. ***Keep it in secrete, do not disclose publicly.*** -| `exchange.pair_whitelist` | [] | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). -| `exchange.pair_blacklist` | [] | List of pairs the bot must absolutely avoid for trading and backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). +| `exchange.pair_whitelist` | [] | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Not used by VolumePairList (see [below](#dynamic-pairlists)). +| `exchange.pair_blacklist` | [] | List of pairs the bot must absolutely avoid for trading and backtesting (see [below](#dynamic-pairlists)). | `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 minutes in which markets are reloaded. @@ -425,10 +425,16 @@ section of the configuration. `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). + * `VolumePairList` does not consider `pair_whitelist`, but builds this automatically based the pairlist configuration. + * Pairs in `pair_blacklist` are not considered for VolumePairList, even if all other filters would match. Example: ```json +"exchange": { + "pair_whitelist": [], + "pair_blacklist": ["BNB/BTC"] +}, "pairlist": { "method": "VolumePairList", "config": { diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 6a8374e6d..93d93263f 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -5,7 +5,7 @@ from jsonschema import Draft4Validator, validators from jsonschema.exceptions import ValidationError, best_match from freqtrade import constants, OperationalException - +from freqtrade.state import RunMode logger = logging.getLogger(__name__) @@ -64,6 +64,7 @@ def validate_config_consistency(conf: Dict[str, Any]) -> None: # validating trailing stoploss _validate_trailing_stoploss(conf) _validate_edge(conf) + _validate_whitelist(conf) def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None: @@ -111,3 +112,15 @@ def _validate_edge(conf: Dict[str, Any]) -> None: "Edge and VolumePairList are incompatible, " "Edge will override whatever pairs VolumePairlist selects." ) + + +def _validate_whitelist(conf: Dict[str, Any]) -> None: + """ + Dynamic whitelist does not require pair_whitelist to be set - however StaticWhitelist does. + """ + if conf.get('runmode', RunMode.OTHER) in [RunMode.OTHER, RunMode.PLOT]: + return + + if (conf.get('pairlist', {}).get('method', 'StaticPairList') == 'StaticPairList' + and not conf.get('exchange', {}).get('pair_whitelist')): + raise OperationalException("StaticPairList requires pair_whitelist to be set.") diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 034f8d386..be1c7ab4e 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -179,6 +179,9 @@ class Configuration: config['exchange']['name'] = self.args["exchange"] logger.info(f"Using exchange {config['exchange']['name']}") + if 'pair_whitelist' not in config['exchange']: + config['exchange']['pair_whitelist'] = [] + if 'user_data_dir' in self.args and self.args["user_data_dir"]: config.update({'user_data_dir': self.args["user_data_dir"]}) elif 'user_data_dir' not in config: diff --git a/freqtrade/constants.py b/freqtrade/constants.py index e8f3f5783..5fdd45916 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -235,7 +235,7 @@ CONF_SCHEMA = { 'ccxt_config': {'type': 'object'}, 'ccxt_async_config': {'type': 'object'} }, - 'required': ['name', 'pair_whitelist'] + 'required': ['name'] }, 'edge': { 'type': 'object', diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7251715a7..a8fc6bc7e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -77,7 +77,7 @@ class FreqtradeBot: self.edge = Edge(self.config, self.exchange, self.strategy) if \ self.config.get('edge', {}).get('enabled', False) else None - self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist'] + self.active_pair_whitelist = self._refresh_whitelist() persistence.init(self.config.get('db_url', None), clean_open_orders=self.config.get('dry_run', False)) @@ -123,21 +123,10 @@ class FreqtradeBot: # Check whether markets have to be reloaded self.exchange._reload_markets() - # Refresh whitelist - self.pairlists.refresh_pairlist() - self.active_pair_whitelist = self.pairlists.whitelist - - # Calculating Edge positioning - if self.edge: - self.edge.calculate() - self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist) - # Query trades from persistence layer trades = Trade.get_open_trades() - # Extend active-pair whitelist with pairs from open trades - # It ensures that tickers are downloaded for open trades - self._extend_whitelist_with_trades(self.active_pair_whitelist, trades) + self.active_pair_whitelist = self._refresh_whitelist(trades) # Refreshing candles self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist), @@ -156,15 +145,28 @@ class FreqtradeBot: Trade.session.flush() if (self.heartbeat_interval - and (arrow.utcnow().timestamp - self._heartbeat_msg > self.heartbeat_interval)): + and (arrow.utcnow().timestamp - self._heartbeat_msg > self.heartbeat_interval)): logger.info(f"Bot heartbeat. PID={getpid()}") self._heartbeat_msg = arrow.utcnow().timestamp - def _extend_whitelist_with_trades(self, whitelist: List[str], trades: List[Any]): + def _refresh_whitelist(self, trades: List[Trade] = []) -> List[str]: """ - Extend whitelist with pairs from open trades + Refresh whitelist from pairlist or edge and extend it with trades. """ - whitelist.extend([trade.pair for trade in trades if trade.pair not in whitelist]) + # Refresh whitelist + self.pairlists.refresh_pairlist() + _whitelist = self.pairlists.whitelist + + # Calculating Edge positioning + if self.edge: + self.edge.calculate() + _whitelist = self.edge.adjust(_whitelist) + + if trades: + # Extend active-pair whitelist with pairs from open trades + # It ensures that tickers are downloaded for open trades + _whitelist.extend([trade.pair for trade in trades if trade.pair not in _whitelist]) + return _whitelist def _create_pair_whitelist(self, pairs: List[str]) -> List[Tuple[str, str]]: """ diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index b9b7977ab..5f53cd17b 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -54,7 +54,7 @@ class VolumePairList(IPairList): """ # Generate dynamic whitelist self._whitelist = self._gen_pair_whitelist( - self._config['stake_currency'], self._sort_key)[:self._number_pairs] + self._config['stake_currency'], self._sort_key) @cached(TTLCache(maxsize=1, ttl=1800)) def _gen_pair_whitelist(self, base_currency: str, key: str) -> List[str]: @@ -91,6 +91,6 @@ class VolumePairList(IPairList): valid_tickers.remove(t) pairs = [s['symbol'] for s in valid_tickers] - logger.info(f"Searching pairs: {self._whitelist}") + logger.info(f"Searching pairs: {pairs[:self._number_pairs]}") return pairs diff --git a/tests/conftest.py b/tests/conftest.py index 305221d6d..4feae6a60 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,13 +55,16 @@ def patched_configuration_load_config_file(mocker, config) -> None: ) -def patch_exchange(mocker, api_mock=None, id='bittrex') -> None: +def patch_exchange(mocker, api_mock=None, id='bittrex', mock_markets=True) -> 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())) + if mock_markets: + mocker.patch('freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=get_markets())) if api_mock: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) @@ -69,8 +72,9 @@ def patch_exchange(mocker, api_mock=None, id='bittrex') -> None: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock()) -def get_patched_exchange(mocker, config, api_mock=None, id='bittrex') -> Exchange: - patch_exchange(mocker, api_mock, id) +def get_patched_exchange(mocker, config, api_mock=None, id='bittrex', + mock_markets=True) -> Exchange: + patch_exchange(mocker, api_mock, id, mock_markets) config["exchange"]["name"] = id try: exchange = ExchangeResolver(id, config).exchange @@ -85,6 +89,11 @@ def patch_wallet(mocker, free=999.9) -> None: )) +def patch_whitelist(mocker, conf) -> None: + mocker.patch('freqtrade.freqtradebot.FreqtradeBot._refresh_whitelist', + MagicMock(return_value=conf['exchange']['pair_whitelist'])) + + def patch_edge(mocker) -> None: # "ETH/BTC", # "LTC/BTC", @@ -120,6 +129,7 @@ def patch_freqtradebot(mocker, config) -> None: patch_exchange(mocker) mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock()) + patch_whitelist(mocker, config) def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: @@ -287,6 +297,10 @@ def ticker_sell_down(): @pytest.fixture def markets(): + return get_markets() + + +def get_markets(): return { 'ETH/BTC': { 'id': 'ethbtc', @@ -369,7 +383,7 @@ def markets(): 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', - 'active': False, + 'active': True, 'precision': { 'price': 8, 'amount': 8, @@ -394,7 +408,7 @@ def markets(): 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', - 'active': False, + 'active': True, 'precision': { 'price': 8, 'amount': 8, @@ -419,7 +433,7 @@ def markets(): 'symbol': 'NEO/BTC', 'base': 'NEO', 'quote': 'BTC', - 'active': False, + 'active': True, 'precision': { 'price': 8, 'amount': 8, @@ -444,7 +458,7 @@ def markets(): 'symbol': 'BTT/BTC', 'base': 'BTT', 'quote': 'BTC', - 'active': True, + 'active': False, 'precision': { 'base': 8, 'quote': 8, @@ -494,7 +508,7 @@ def markets(): 'symbol': 'LTC/USDT', 'base': 'LTC', 'quote': 'USDT', - 'active': True, + 'active': False, 'precision': { 'amount': 8, 'price': 8 diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 95382768a..48ef2affd 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -533,21 +533,22 @@ def test_refresh_backtest_ohlcv_data(mocker, default_conf, markets, caplog, test def test_download_data_no_markets(mocker, default_conf, caplog, testdatadir): dl_mock = mocker.patch('freqtrade.data.history.download_pair_history', MagicMock()) + + ex = get_patched_exchange(mocker, default_conf) mocker.patch( 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) ) - ex = get_patched_exchange(mocker, default_conf) timerange = TimeRange.parse_timerange("20190101-20190102") - unav_pairs = refresh_backtest_ohlcv_data(exchange=ex, pairs=["ETH/BTC", "XRP/BTC"], + unav_pairs = refresh_backtest_ohlcv_data(exchange=ex, pairs=["BTT/BTC", "LTC/USDT"], timeframes=["1m", "5m"], dl_path=testdatadir, timerange=timerange, erase=False ) assert dl_mock.call_count == 0 - assert "ETH/BTC" in unav_pairs - assert "XRP/BTC" in unav_pairs - assert log_has("Skipping pair ETH/BTC...", caplog) + assert "BTT/BTC" in unav_pairs + assert "LTC/USDT" in unav_pairs + assert log_has("Skipping pair BTT/BTC...", caplog) def test_refresh_backtest_trades_data(mocker, default_conf, markets, caplog, testdatadir): diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 1e0a5fdc3..d3f50c6da 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -177,16 +177,11 @@ def test_symbol_amount_prec(default_conf, mocker): ''' Test rounds down to 4 Decimal places ''' - api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={ - 'ETH/BTC': '', 'LTC/BTC': '', 'XRP/BTC': '', 'NEO/BTC': '' - }) - mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='binance')) markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'amount': 4}}}) - type(api_mock).markets = markets - exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange = get_patched_exchange(mocker, default_conf, id="binance") + mocker.patch('freqtrade.exchange.Exchange.markets', markets) amount = 2.34559 pair = 'ETH/BTC' @@ -198,16 +193,10 @@ def test_symbol_price_prec(default_conf, mocker): ''' Test rounds up to 4 decimal places ''' - api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={ - 'ETH/BTC': '', 'LTC/BTC': '', 'XRP/BTC': '', 'NEO/BTC': '' - }) - mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='binance')) - markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': 4}}}) - type(api_mock).markets = markets - exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange = get_patched_exchange(mocker, default_conf, id="binance") + mocker.patch('freqtrade.exchange.Exchange.markets', markets) price = 2.34559 pair = 'ETH/BTC' @@ -279,7 +268,7 @@ def test__load_markets(default_conf, mocker, caplog): api_mock.load_markets = MagicMock(return_value=expected_return) type(api_mock).markets = expected_return default_conf['exchange']['pair_whitelist'] = ['ETH/BTC'] - ex = get_patched_exchange(mocker, default_conf, api_mock, id="binance") + ex = get_patched_exchange(mocker, default_conf, api_mock, id="binance", mock_markets=False) assert ex.markets == expected_return @@ -294,7 +283,8 @@ def test__reload_markets(default_conf, mocker, caplog): 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 = get_patched_exchange(mocker, default_conf, api_mock, id="binance", + mock_markets=False) exchange._last_markets_refresh = arrow.utcnow().timestamp updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} @@ -1715,15 +1705,16 @@ def test_get_valid_pair_combination(default_conf, mocker, markets): 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']), # active markets ([], [], False, True, - ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/USD', 'LTC/USDT', - 'TKN/BTC', 'XLTCUSDT']), + ['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'NEO/BTC', + 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC']), # all pairs ([], [], True, False, ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC']), # active pairs ([], [], True, True, - ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/USD', 'LTC/USDT', 'TKN/BTC']), + ['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'NEO/BTC', + 'TKN/BTC', 'XRP/BTC']), # all markets, base=ETH, LTC (['ETH', 'LTC'], [], False, False, ['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT']), diff --git a/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py index 411ae60a3..929fc0ba0 100644 --- a/tests/pairlist/test_pairlist.py +++ b/tests/pairlist/test_pairlist.py @@ -80,7 +80,7 @@ def test_refresh_pairlist_dynamic(mocker, markets, tickers, whitelist_conf): freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf) # argument: use the whitelist dynamically by exchange-volume - whitelist = ['ETH/BTC', 'TKN/BTC', 'BTT/BTC'] + whitelist = ['ETH/BTC', 'TKN/BTC', 'LTC/BTC'] freqtradebot.pairlists.refresh_pairlist() assert whitelist == freqtradebot.pairlists.whitelist @@ -108,12 +108,12 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): @pytest.mark.parametrize("precision_filter,base_currency,key,whitelist_result", [ - (False, "BTC", "quoteVolume", ['ETH/BTC', 'TKN/BTC', 'BTT/BTC']), - (False, "BTC", "bidVolume", ['BTT/BTC', 'TKN/BTC', 'ETH/BTC']), - (False, "USDT", "quoteVolume", ['ETH/USDT', 'LTC/USDT']), + (False, "BTC", "quoteVolume", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC']), + (False, "BTC", "bidVolume", ['LTC/BTC', 'TKN/BTC', 'ETH/BTC']), + (False, "USDT", "quoteVolume", ['ETH/USDT']), (False, "ETH", "quoteVolume", []), # this replaces tests that were removed from test_exchange - (True, "BTC", "quoteVolume", ["ETH/BTC", "TKN/BTC"]), - (True, "BTC", "bidVolume", ["TKN/BTC", "ETH/BTC"]) + (True, "BTC", "quoteVolume", ["LTC/BTC", "ETH/BTC", "TKN/BTC"]), + (True, "BTC", "bidVolume", ["LTC/BTC", "TKN/BTC", "ETH/BTC"]) ]) def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, markets, tickers, base_currency, key, whitelist_result, precision_filter) -> None: @@ -127,7 +127,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, markets, tickers, freqtrade.pairlists._precision_filter = precision_filter freqtrade.config['stake_currency'] = base_currency whitelist = freqtrade.pairlists._gen_pair_whitelist(base_currency=base_currency, key=key) - assert whitelist == whitelist_result + assert sorted(whitelist) == sorted(whitelist_result) def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None: @@ -160,7 +160,7 @@ def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): (['ETH/BTC', 'TKN/BTC', 'TRX/ETH'], "is not compatible with exchange"), # TRX/ETH wrong stake (['ETH/BTC', 'TKN/BTC', 'BCH/BTC'], "is not compatible with exchange"), # BCH/BTC not available (['ETH/BTC', 'TKN/BTC', 'BLK/BTC'], "is not compatible with exchange"), # BLK/BTC in blacklist - (['ETH/BTC', 'TKN/BTC', 'LTC/BTC'], "Market is not active") # LTC/BTC is inactive + (['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active") # BTT/BTC is inactive ]) def test_validate_whitelist(mocker, whitelist_conf, markets, pairlist, whitelist, caplog, log_message): diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 66468927f..df2261c1f 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -9,12 +9,11 @@ from numpy import isnan from freqtrade import DependencyException, TemporaryError from freqtrade.edge import PairInfo -from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPC, RPCException from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.state import State -from tests.conftest import patch_exchange, patch_get_signal +from tests.conftest import patch_get_signal, get_patched_freqtradebot # Functions for recurrent object patching @@ -26,17 +25,15 @@ def prec_satoshi(a, b) -> float: # Unit tests -def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: +def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -98,17 +95,15 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: } == results[0] -def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: - patch_exchange(mocker) +def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -134,7 +129,6 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: def test_rpc_daily_profit(default_conf, update, ticker, fee, limit_buy_order, limit_sell_order, markets, mocker) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -143,7 +137,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -181,22 +175,20 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, markets, mocker) -> None: + limit_buy_order, limit_sell_order, mocker) -> None: mocker.patch.multiple( 'freqtrade.rpc.fiat_convert.Market', ticker=MagicMock(return_value={'price_usd': 15000.0}), ) - patch_exchange(mocker) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -267,9 +259,8 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, # Test that rpc_trade_statistics can handle trades that lacks # trade.open_rate (it is set to None) -def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, +def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, ticker_sell_up, limit_buy_order, limit_sell_order): - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.rpc.fiat_convert.Market', ticker=MagicMock(return_value={'price_usd': 15000.0}), @@ -281,10 +272,9 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -343,7 +333,6 @@ def test_rpc_balance_handle_error(default_conf, mocker): 'freqtrade.rpc.fiat_convert.Market', ticker=MagicMock(return_value={'price_usd': 15000.0}), ) - patch_exchange(mocker) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( @@ -352,7 +341,7 @@ def test_rpc_balance_handle_error(default_conf, mocker): get_ticker=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx')) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -394,7 +383,6 @@ def test_rpc_balance_handle(default_conf, mocker): 'freqtrade.rpc.fiat_convert.Market', ticker=MagicMock(return_value={'price_usd': 15000.0}), ) - patch_exchange(mocker) mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( @@ -406,7 +394,7 @@ def test_rpc_balance_handle(default_conf, mocker): side_effect=lambda a, b: f"{b}/{a}" if a == "PAX" else f"{a}/{b}") ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -438,14 +426,13 @@ def test_rpc_balance_handle(default_conf, mocker): def test_rpc_start(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -460,14 +447,13 @@ def test_rpc_start(mocker, default_conf) -> None: def test_rpc_stop(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -483,14 +469,13 @@ def test_rpc_stop(mocker, default_conf) -> None: def test_rpc_stopbuy(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -501,8 +486,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: assert freqtradebot.config['max_open_trades'] == 0 -def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: - patch_exchange(mocker) +def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) cancel_order_mock = MagicMock() @@ -518,10 +502,9 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: } ), get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -606,18 +589,16 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: def test_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, markets, mocker) -> None: - patch_exchange(mocker) + limit_sell_order, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -641,18 +622,16 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, assert prec_satoshi(res[0]['profit'], 6.2) -def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: - patch_exchange(mocker) +def test_rpc_count(mocker, default_conf, ticker, fee) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -665,9 +644,8 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: assert counts["current"] == 1 -def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order) -> None: +def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None: default_conf['forcebuy_enable'] = True - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) buy_mm = MagicMock(return_value={'id': limit_buy_order['id']}) mocker.patch.multiple( @@ -675,11 +653,10 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order get_balances=MagicMock(return_value=ticker), get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets), buy=buy_mm ) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -704,7 +681,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order # Test not buying default_conf['stake_amount'] = 0.0000001 - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'TKN/BTC' @@ -715,10 +692,9 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order def test_rpcforcebuy_stopped(mocker, default_conf) -> None: default_conf['forcebuy_enable'] = True default_conf['initial_state'] = 'stopped' - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -727,10 +703,9 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: def test_rpcforcebuy_disabled(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -739,10 +714,9 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: def test_rpc_whitelist(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() assert ret['method'] == 'StaticPairList' @@ -750,14 +724,13 @@ def test_rpc_whitelist(mocker, default_conf) -> None: def test_rpc_whitelist_dynamic(mocker, default_conf) -> None: - patch_exchange(mocker) default_conf['pairlist'] = {'method': 'VolumePairList', 'config': {'number_assets': 4} } mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() assert ret['method'] == 'VolumePairList' @@ -766,10 +739,9 @@ def test_rpc_whitelist_dynamic(mocker, default_conf) -> None: def test_rpc_blacklist(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_blacklist(None) assert ret['method'] == 'StaticPairList' @@ -785,23 +757,21 @@ def test_rpc_blacklist(mocker, default_conf) -> None: def test_rpc_edge_disabled(mocker, default_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(freqtradebot) with pytest.raises(RPCException, match=r'Edge is not enabled.'): rpc._rpc_edge() def test_rpc_edge_enabled(mocker, edge_conf) -> None: - patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock( return_value={ 'E/F': PairInfo(-0.02, 0.66, 3.71, 0.50, 1.71, 10, 60), } )) - freqtradebot = FreqtradeBot(edge_conf) + freqtradebot = get_patched_freqtradebot(mocker, edge_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_edge() diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index a776ad5df..766511d2d 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -22,7 +22,7 @@ from freqtrade.rpc.telegram import Telegram, authorized_only from freqtrade.state import State from freqtrade.strategy.interface import SellType from tests.conftest import (get_patched_freqtradebot, log_has, patch_exchange, - patch_get_signal) + patch_get_signal, patch_whitelist) class DummyCls(Telegram): @@ -143,17 +143,15 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: assert log_has('Exception occurred within Telegram module', caplog) -def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: +def test_status(default_conf, update, mocker, fee, ticker,) -> None: update.message.chat.id = 123 default_conf['telegram']['enabled'] = False default_conf['telegram']['chat_id'] = 123 - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(markets) ) msg_mock = MagicMock() status_table = MagicMock() @@ -184,9 +182,8 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: _status_table=status_table, _send_msg=msg_mock ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -204,13 +201,11 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: assert status_table.call_count == 1 -def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> None: - patch_exchange(mocker) +def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(markets) ) msg_mock = MagicMock() status_table = MagicMock() @@ -220,9 +215,9 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No _status_table=status_table, _send_msg=msg_mock ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -256,14 +251,12 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0] -def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) -> None: - patch_exchange(mocker) +def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, buy=MagicMock(return_value={'id': 'mocked_order_id'}), get_fee=fee, - markets=PropertyMock(markets) ) msg_mock = MagicMock() mocker.patch.multiple( @@ -271,10 +264,9 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) _init=MagicMock(), _send_msg=msg_mock ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) default_conf['stake_amount'] = 15.0 - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -307,8 +299,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, markets, mocker) -> None: - patch_exchange(mocker) + limit_sell_order, mocker) -> None: default_conf['max_open_trades'] = 1 mocker.patch( 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', @@ -318,7 +309,6 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(markets) ) msg_mock = MagicMock() mocker.patch.multiple( @@ -326,9 +316,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, _init=MagicMock(), _send_msg=msg_mock ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -382,7 +371,6 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker @@ -393,9 +381,8 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: _init=MagicMock(), _send_msg=msg_mock ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -420,14 +407,12 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, markets, mocker) -> None: - patch_exchange(mocker) + limit_buy_order, limit_sell_order, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(markets) ) msg_mock = MagicMock() mocker.patch.multiple( @@ -435,9 +420,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, _init=MagicMock(), _send_msg=msg_mock ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -724,16 +708,16 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None: def test_forcesell_handle(default_conf, update, ticker, fee, - ticker_sell_up, markets, mocker) -> None: + ticker_sell_up, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) patch_exchange(mocker) + patch_whitelist(mocker, default_conf) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets), ) freqtradebot = FreqtradeBot(default_conf) @@ -775,17 +759,18 @@ def test_forcesell_handle(default_conf, update, ticker, fee, def test_forcesell_down_handle(default_conf, update, ticker, fee, - ticker_sell_down, markets, mocker) -> None: + ticker_sell_down, mocker) -> None: mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) patch_exchange(mocker) + patch_whitelist(mocker, default_conf) + mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets), ) freqtradebot = FreqtradeBot(default_conf) @@ -830,17 +815,17 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, } == last_msg -def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker) -> None: +def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) + patch_whitelist(mocker, default_conf) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets), ) default_conf['max_open_trades'] = 4 freqtradebot = FreqtradeBot(default_conf) @@ -885,9 +870,8 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: _init=MagicMock(), _send_msg=msg_mock ) - patch_exchange(mocker) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -980,8 +964,7 @@ def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> Non def test_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, markets, mocker) -> None: - patch_exchange(mocker) + limit_buy_order, limit_sell_order, mocker) -> None: msg_mock = MagicMock() mocker.patch.multiple( 'freqtrade.rpc.telegram.Telegram', @@ -992,10 +975,8 @@ def test_performance_handle(default_conf, update, ticker, fee, 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(markets), ) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -1018,8 +999,7 @@ def test_performance_handle(default_conf, update, ticker, fee, assert 'ETH/BTC\t6.20% (1)' in msg_mock.call_args_list[0][0][0] -def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> None: - patch_exchange(mocker) +def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: msg_mock = MagicMock() mocker.patch.multiple( 'freqtrade.rpc.telegram.Telegram', @@ -1030,10 +1010,9 @@ 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'}), - markets=PropertyMock(markets) + get_fee=fee, ) - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - freqtradebot = FreqtradeBot(default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 2aa805fe6..545dd5df4 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -729,6 +729,30 @@ def test_validate_edge(edge_conf): validate_config_consistency(edge_conf) +def test_validate_whitelist(default_conf): + default_conf['runmode'] = RunMode.DRY_RUN + # Test regular case - has whitelist and uses StaticPairlist + validate_config_consistency(default_conf) + conf = deepcopy(default_conf) + del conf['exchange']['pair_whitelist'] + # Test error case + with pytest.raises(OperationalException, + match="StaticPairList requires pair_whitelist to be set."): + + validate_config_consistency(conf) + + conf = deepcopy(default_conf) + + conf.update({"pairlist": { + "method": "VolumePairList", + }}) + # Dynamic whitelist should not care about pair_whitelist + validate_config_consistency(conf) + del conf['exchange']['pair_whitelist'] + + validate_config_consistency(conf) + + def test_load_config_test_comments() -> None: """ Load config with comments @@ -801,7 +825,7 @@ def test_pairlist_resolving(): args = Arguments(arglist).get_parsed_arg() - configuration = Configuration(args) + configuration = Configuration(args, RunMode.OTHER) config = configuration.get_config() assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] @@ -895,7 +919,7 @@ def test_pairlist_resolving_fallback(mocker): # Fix flaky tests if config.json exists args["config"] = None - configuration = Configuration(args) + configuration = Configuration(args, RunMode.OTHER) config = configuration.get_config() assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 8aefaba17..30f9ba0a4 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -23,7 +23,7 @@ from freqtrade.strategy.interface import SellCheckTuple, SellType from freqtrade.worker import Worker from tests.conftest import (get_patched_freqtradebot, get_patched_worker, log_has, log_has_re, patch_edge, patch_exchange, - patch_get_signal, patch_wallet) + patch_get_signal, patch_wallet, patch_whitelist) def patch_RPCManager(mocker) -> MagicMock: @@ -1247,11 +1247,10 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, - markets, limit_buy_order, limit_sell_order) -> None: + limit_buy_order, limit_sell_order) -> None: # When trailing stoploss is set stoploss_limit = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=MagicMock(return_value={ @@ -1262,7 +1261,6 @@ 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, - markets=PropertyMock(return_value=markets), stoploss_limit=stoploss_limit ) @@ -1272,7 +1270,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, # disabling ROI default_conf['minimal_roi']['0'] = 999999999 - freqtrade = FreqtradeBot(default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf) # enabling stoploss on exchange freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -1825,20 +1823,18 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, def test_handle_trade_roi(default_conf, ticker, limit_buy_order, - fee, mocker, markets, caplog) -> None: + fee, mocker, caplog) -> None: caplog.set_level(logging.DEBUG) patch_RPCManager(mocker) - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtrade, value=(True, False)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) @@ -1859,20 +1855,18 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, def test_handle_trade_use_sell_signal( - default_conf, ticker, limit_buy_order, fee, mocker, markets, caplog) -> None: + default_conf, ticker, limit_buy_order, fee, mocker, caplog) -> None: # use_sell_signal is True buy default caplog.set_level(logging.DEBUG) patch_RPCManager(mocker) - patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - markets=PropertyMock(return_value=markets) ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.create_trades() @@ -2237,6 +2231,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc get_fee=fee, markets=PropertyMock(return_value=markets) ) + patch_whitelist(mocker, default_conf) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2283,6 +2278,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, get_fee=fee, markets=PropertyMock(return_value=markets) ) + patch_whitelist(mocker, default_conf) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2332,6 +2328,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe get_fee=fee, markets=PropertyMock(return_value=markets) ) + patch_whitelist(mocker, default_conf) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2648,6 +2645,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee, get_fee=fee, markets=PropertyMock(return_value=markets) ) + patch_whitelist(mocker, default_conf) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2815,14 +2813,13 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke assert trade.sell_reason == SellType.SELL_SIGNAL.value -def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, markets, mocker, caplog) -> None: +def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_ticker=ticker, get_fee=fee, - markets=PropertyMock(return_value=markets) ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2852,7 +2849,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, markets, mock assert log_has(f"Pair {trade.pair} is currently locked.", caplog) -def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, mocker) -> None: +def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2864,7 +2861,6 @@ 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, - markets=PropertyMock(return_value=markets) ) default_conf['ask_strategy'] = { 'ignore_roi_if_buy_signal': True @@ -2886,7 +2882,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m assert trade.sell_reason == SellType.ROI.value -def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, mocker) -> None: +def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2898,9 +2894,9 @@ 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, - markets=PropertyMock(return_value=markets), ) default_conf['trailing_stop'] = True + patch_whitelist(mocker, default_conf) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) @@ -2938,7 +2934,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets, +def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, caplog, mocker) -> None: buy_price = limit_buy_order['price'] patch_RPCManager(mocker) @@ -2952,10 +2948,11 @@ 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, - markets=PropertyMock(return_value=markets), ) default_conf['trailing_stop'] = True default_conf['trailing_stop_positive'] = 0.01 + patch_whitelist(mocker, default_conf) + freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) @@ -2995,7 +2992,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, - caplog, mocker, markets) -> None: + caplog, mocker) -> None: buy_price = limit_buy_order['price'] patch_RPCManager(mocker) patch_exchange(mocker) @@ -3008,9 +3005,8 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - markets=PropertyMock(return_value=markets), ) - + patch_whitelist(mocker, default_conf) default_conf['trailing_stop'] = True default_conf['trailing_stop_positive'] = 0.01 default_conf['trailing_stop_positive_offset'] = 0.011 @@ -3055,7 +3051,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee, - caplog, mocker, markets) -> None: + caplog, mocker) -> None: buy_price = limit_buy_order['price'] # buy_price: 0.00001099 @@ -3070,9 +3066,8 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee, }), buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, - markets=PropertyMock(return_value=markets), ) - + patch_whitelist(mocker, default_conf) default_conf['trailing_stop'] = True default_conf['trailing_stop_positive'] = 0.05 default_conf['trailing_stop_positive_offset'] = 0.055 diff --git a/tests/test_utils.py b/tests/test_utils.py index f64a6924a..7d6b82809 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -188,8 +188,8 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ("Exchange Bittrex has 8 active markets: " - "BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/USD, LTC/USDT, TKN/BTC, XLTCUSDT.\n" + assert ("Exchange Bittrex has 9 active markets: " + "BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, NEO/BTC, TKN/BTC, XLTCUSDT, XRP/BTC.\n" in captured.out) patch_exchange(mocker, api_mock=api_mock, id="binance") @@ -202,7 +202,7 @@ def test_list_markets(mocker, markets, capsys): pargs['config'] = None start_list_markets(pargs, False) captured = capsys.readouterr() - assert re.match("\nExchange Binance has 8 active markets:\n", + assert re.match("\nExchange Binance has 9 active markets:\n", captured.out) patch_exchange(mocker, api_mock=api_mock, id="bittrex") @@ -227,8 +227,8 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), True) captured = capsys.readouterr() - assert ("Exchange Bittrex has 7 active pairs: " - "BLK/BTC, BTT/BTC, ETH/BTC, ETH/USDT, LTC/USD, LTC/USDT, TKN/BTC.\n" + assert ("Exchange Bittrex has 8 active pairs: " + "BLK/BTC, ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, NEO/BTC, TKN/BTC, XRP/BTC.\n" in captured.out) # Test list-pairs subcommand with --all: all pairs @@ -254,7 +254,7 @@ def test_list_markets(mocker, markets, capsys): start_list_markets(get_args(args), False) captured = capsys.readouterr() assert ("Exchange Bittrex has 5 active markets with ETH, LTC as base currencies: " - "ETH/BTC, ETH/USDT, LTC/USD, LTC/USDT, XLTCUSDT.\n" + "ETH/BTC, ETH/USDT, LTC/BTC, LTC/USD, XLTCUSDT.\n" in captured.out) # active markets, base=LTC @@ -267,7 +267,7 @@ def test_list_markets(mocker, markets, capsys): start_list_markets(get_args(args), False) captured = capsys.readouterr() assert ("Exchange Bittrex has 3 active markets with LTC as base currency: " - "LTC/USD, LTC/USDT, XLTCUSDT.\n" + "LTC/BTC, LTC/USD, XLTCUSDT.\n" in captured.out) # active markets, quote=USDT, USD @@ -279,8 +279,8 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ("Exchange Bittrex has 4 active markets with USDT, USD as quote currencies: " - "ETH/USDT, LTC/USD, LTC/USDT, XLTCUSDT.\n" + assert ("Exchange Bittrex has 3 active markets with USDT, USD as quote currencies: " + "ETH/USDT, LTC/USD, XLTCUSDT.\n" in captured.out) # active markets, quote=USDT @@ -292,8 +292,8 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ("Exchange Bittrex has 3 active markets with USDT as quote currency: " - "ETH/USDT, LTC/USDT, XLTCUSDT.\n" + assert ("Exchange Bittrex has 2 active markets with USDT as quote currency: " + "ETH/USDT, XLTCUSDT.\n" in captured.out) # active markets, base=LTC, quote=USDT @@ -305,21 +305,21 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ("Exchange Bittrex has 2 active markets with LTC as base currency and " - "with USDT as quote currency: LTC/USDT, XLTCUSDT.\n" + assert ("Exchange Bittrex has 1 active market with LTC as base currency and " + "with USDT as quote currency: XLTCUSDT.\n" in captured.out) # active pairs, base=LTC, quote=USDT args = [ '--config', 'config.json.example', "list-pairs", - "--base", "LTC", "--quote", "USDT", + "--base", "LTC", "--quote", "USD", "--print-list", ] start_list_markets(get_args(args), True) captured = capsys.readouterr() assert ("Exchange Bittrex has 1 active pair with LTC as base currency and " - "with USDT as quote currency: LTC/USDT.\n" + "with USD as quote currency: LTC/USD.\n" in captured.out) # active markets, base=LTC, quote=USDT, NONEXISTENT @@ -331,8 +331,8 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ("Exchange Bittrex has 2 active markets with LTC as base currency and " - "with USDT, NONEXISTENT as quote currencies: LTC/USDT, XLTCUSDT.\n" + assert ("Exchange Bittrex has 1 active market with LTC as base currency and " + "with USDT, NONEXISTENT as quote currencies: XLTCUSDT.\n" in captured.out) # active markets, base=LTC, quote=NONEXISTENT @@ -355,7 +355,7 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ("Exchange Bittrex has 8 active markets:\n" + assert ("Exchange Bittrex has 9 active markets:\n" in captured.out) # Test tabular output, no markets found @@ -378,7 +378,8 @@ def test_list_markets(mocker, markets, capsys): ] start_list_markets(get_args(args), False) captured = capsys.readouterr() - assert ('["BLK/BTC","BTT/BTC","ETH/BTC","ETH/USDT","LTC/USD","LTC/USDT","TKN/BTC","XLTCUSDT"]' + assert ('["BLK/BTC","ETH/BTC","ETH/USDT","LTC/BTC","LTC/USD","NEO/BTC",' + '"TKN/BTC","XLTCUSDT","XRP/BTC"]' in captured.out) # Test --print-csv @@ -391,7 +392,7 @@ def test_list_markets(mocker, markets, capsys): captured = capsys.readouterr() assert ("Id,Symbol,Base,Quote,Active,Is pair" in captured.out) assert ("blkbtc,BLK/BTC,BLK,BTC,True,True" in captured.out) - assert ("BTTBTC,BTT/BTC,BTT,BTC,True,True" in captured.out) + assert ("USD-LTC,LTC/USD,LTC,USD,True,True" in captured.out) # Test --one-column args = [ @@ -402,7 +403,7 @@ def test_list_markets(mocker, markets, capsys): start_list_markets(get_args(args), False) captured = capsys.readouterr() assert re.search(r"^BLK/BTC$", captured.out, re.MULTILINE) - assert re.search(r"^BTT/BTC$", captured.out, re.MULTILINE) + assert re.search(r"^LTC/USD$", captured.out, re.MULTILINE) def test_create_datadir_failed(caplog):