From 326e3d1f8ef38a1d64d79d207b28e325413f10d4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 6 Jan 2022 14:12:00 +0100 Subject: [PATCH 1/3] Selectively convert quote to base volume in volumepairlist --- freqtrade/exchange/exchange.py | 2 ++ freqtrade/exchange/ftx.py | 1 + freqtrade/plugins/pairlist/VolumePairList.py | 14 +++++++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index c694756f5..36f5ecd39 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -67,6 +67,8 @@ class Exchange: "ohlcv_params": {}, "ohlcv_candle_limit": 500, "ohlcv_partial_candle": True, + # Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency + "ohlcv_volume_currency": "base", # "base" or "quote" "trades_pagination": "time", # Possible are "time" or "id" "trades_pagination_arg": "since", "l2_limit_range": None, diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 6cd549d60..e9eb2fe19 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -19,6 +19,7 @@ class Ftx(Exchange): _ft_has: Dict = { "stoploss_on_exchange": True, "ohlcv_candle_limit": 1500, + "ohlcv_volume_currency": "quote", } def market_is_tradable(self, market: Dict[str, Any]) -> bool: diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 204bc7bea..5d78422bb 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -184,12 +184,16 @@ class VolumePairList(IPairList): ] if (p['symbol'], self._lookback_timeframe) in candles else None # in case of candle data calculate typical price and quoteVolume for candle if pair_candles is not None and not pair_candles.empty: - pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] - + pair_candles['close']) / 3 - pair_candles['quoteVolume'] = ( - pair_candles['volume'] * pair_candles['typical_price'] - ) + if self._exchange._ft_has["ohlcv_volume_currency"] == "base": + pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + + pair_candles['close']) / 3 + pair_candles['quoteVolume'] = ( + pair_candles['volume'] * pair_candles['typical_price'] + ) + else: + # Exchange ohlcv data is in quote volume already. + pair_candles['quoteVolume'] = pair_candles['volume'] # ensure that a rolling sum over the lookback_period is built # if pair_candles contains more candles than lookback_period quoteVolume = (pair_candles['quoteVolume'] From 24ec78b11cfcd1078c3fc594c99e762d9ea21875 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 6 Jan 2022 16:13:10 +0100 Subject: [PATCH 2/3] Quote-volumelist fix for gateio --- freqtrade/exchange/gateio.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 018248a99..7e1f21921 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -21,6 +21,7 @@ class Gateio(Exchange): _ft_has: Dict = { "ohlcv_candle_limit": 1000, + "ohlcv_volume_currency": "quote", } _headers = {'X-Gate-Channel-Id': 'freqtrade'} From 72f486289a7f2275416e0f7e3732966613efc76f Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 6 Jan 2022 16:25:01 +0100 Subject: [PATCH 3/3] Update Volumepairlist test --- tests/plugins/test_pairlist.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index f7ff495ac..76a067cee 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -565,36 +565,41 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t assert log_has_re(r'^Removed .* from whitelist, because volatility.*$', caplog) -@pytest.mark.parametrize("pairlists,base_currency,volumefilter_result", [ +@pytest.mark.parametrize("pairlists,base_currency,exchange,volumefilter_result", [ # default refresh of 1800 to small for daily candle lookback ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_days": 1}], - "BTC", "default_refresh_too_short"), # OperationalException expected + "BTC", "binance", "default_refresh_too_short"), # OperationalException expected # ambigous configuration with lookback days and period ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_days": 1, "lookback_period": 1}], - "BTC", "lookback_days_and_period"), # OperationalException expected + "BTC", "binance", "lookback_days_and_period"), # OperationalException expected # negative lookback period ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_timeframe": "1d", "lookback_period": -1}], - "BTC", "lookback_period_negative"), # OperationalException expected + "BTC", "binance", "lookback_period_negative"), # OperationalException expected # lookback range exceedes exchange limit ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_timeframe": "1m", "lookback_period": 2000, "refresh_period": 3600}], - "BTC", 'lookback_exceeds_exchange_request_size'), # OperationalException expected + "BTC", "binance", "lookback_exceeds_exchange_request_size"), # OperationalException expected # expecing pairs as given ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}], - "BTC", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']), + "BTC", "binance", ['LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC', 'HOT/BTC']), # expecting pairs from default tickers, because 1h candles are not available ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}], - "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']), + "BTC", "binance", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']), + # ftx data is already in Quote currency, therefore won't require conversion + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}], + "BTC", "ftx", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']), ]) def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history, - pairlists, base_currency, volumefilter_result, caplog) -> None: + pairlists, base_currency, exchange, volumefilter_result) -> None: whitelist_conf['pairlists'] = pairlists whitelist_conf['stake_currency'] = base_currency + whitelist_conf['exchange']['name'] = exchange ohlcv_history_high_vola = ohlcv_history.copy() ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090 @@ -603,9 +608,14 @@ def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history_medium_volume = ohlcv_history.copy() ohlcv_history_medium_volume.loc[ohlcv_history_medium_volume.index == 2, 'volume'] = 5 - # create candles for high volume with all candles high volume + # create candles for high volume with all candles high volume, but very low price. ohlcv_history_high_volume = ohlcv_history.copy() ohlcv_history_high_volume.loc[:, 'volume'] = 10 + ohlcv_history_high_volume.loc[:, 'low'] = ohlcv_history_high_volume.loc[:, 'low'] * 0.01 + ohlcv_history_high_volume.loc[:, 'high'] = ohlcv_history_high_volume.loc[:, 'high'] * 0.01 + ohlcv_history_high_volume.loc[:, 'close'] = ohlcv_history_high_volume.loc[:, 'close'] * 0.01 + + mocker.patch('freqtrade.exchange.ftx.Ftx.market_is_tradable', return_value=True) ohlcv_data = { ('ETH/BTC', '1d'): ohlcv_history,