Merge pull request #6173 from freqtrade/volume_quote_workaround
Selectively convert quote to base volume in volumepairlist
This commit is contained in:
commit
b77943af0d
@ -67,6 +67,8 @@ class Exchange:
|
|||||||
"ohlcv_params": {},
|
"ohlcv_params": {},
|
||||||
"ohlcv_candle_limit": 500,
|
"ohlcv_candle_limit": 500,
|
||||||
"ohlcv_partial_candle": True,
|
"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": "time", # Possible are "time" or "id"
|
||||||
"trades_pagination_arg": "since",
|
"trades_pagination_arg": "since",
|
||||||
"l2_limit_range": None,
|
"l2_limit_range": None,
|
||||||
|
@ -19,6 +19,7 @@ class Ftx(Exchange):
|
|||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
"ohlcv_candle_limit": 1500,
|
"ohlcv_candle_limit": 1500,
|
||||||
|
"ohlcv_volume_currency": "quote",
|
||||||
}
|
}
|
||||||
|
|
||||||
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||||
|
@ -21,6 +21,7 @@ class Gateio(Exchange):
|
|||||||
|
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"ohlcv_candle_limit": 1000,
|
"ohlcv_candle_limit": 1000,
|
||||||
|
"ohlcv_volume_currency": "quote",
|
||||||
}
|
}
|
||||||
|
|
||||||
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
||||||
|
@ -184,12 +184,16 @@ class VolumePairList(IPairList):
|
|||||||
] if (p['symbol'], self._lookback_timeframe) in candles else None
|
] if (p['symbol'], self._lookback_timeframe) in candles else None
|
||||||
# in case of candle data calculate typical price and quoteVolume for candle
|
# in case of candle data calculate typical price and quoteVolume for candle
|
||||||
if pair_candles is not None and not pair_candles.empty:
|
if pair_candles is not None and not pair_candles.empty:
|
||||||
|
if self._exchange._ft_has["ohlcv_volume_currency"] == "base":
|
||||||
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
|
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
|
||||||
+ pair_candles['close']) / 3
|
+ pair_candles['close']) / 3
|
||||||
|
|
||||||
pair_candles['quoteVolume'] = (
|
pair_candles['quoteVolume'] = (
|
||||||
pair_candles['volume'] * pair_candles['typical_price']
|
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
|
# ensure that a rolling sum over the lookback_period is built
|
||||||
# if pair_candles contains more candles than lookback_period
|
# if pair_candles contains more candles than lookback_period
|
||||||
quoteVolume = (pair_candles['quoteVolume']
|
quoteVolume = (pair_candles['quoteVolume']
|
||||||
|
@ -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)
|
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
|
# default refresh of 1800 to small for daily candle lookback
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_days": 1}],
|
"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
|
# ambigous configuration with lookback days and period
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_days": 1, "lookback_period": 1}],
|
"lookback_days": 1, "lookback_period": 1}],
|
||||||
"BTC", "lookback_days_and_period"), # OperationalException expected
|
"BTC", "binance", "lookback_days_and_period"), # OperationalException expected
|
||||||
# negative lookback period
|
# negative lookback period
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1d", "lookback_period": -1}],
|
"lookback_timeframe": "1d", "lookback_period": -1}],
|
||||||
"BTC", "lookback_period_negative"), # OperationalException expected
|
"BTC", "binance", "lookback_period_negative"), # OperationalException expected
|
||||||
# lookback range exceedes exchange limit
|
# lookback range exceedes exchange limit
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1m", "lookback_period": 2000, "refresh_period": 3600}],
|
"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
|
# expecing pairs as given
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
|
"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
|
# expecting pairs from default tickers, because 1h candles are not available
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}],
|
"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,
|
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['pairlists'] = pairlists
|
||||||
whitelist_conf['stake_currency'] = base_currency
|
whitelist_conf['stake_currency'] = base_currency
|
||||||
|
whitelist_conf['exchange']['name'] = exchange
|
||||||
|
|
||||||
ohlcv_history_high_vola = ohlcv_history.copy()
|
ohlcv_history_high_vola = ohlcv_history.copy()
|
||||||
ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090
|
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 = ohlcv_history.copy()
|
||||||
ohlcv_history_medium_volume.loc[ohlcv_history_medium_volume.index == 2, 'volume'] = 5
|
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 = ohlcv_history.copy()
|
||||||
ohlcv_history_high_volume.loc[:, 'volume'] = 10
|
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 = {
|
ohlcv_data = {
|
||||||
('ETH/BTC', '1d'): ohlcv_history,
|
('ETH/BTC', '1d'): ohlcv_history,
|
||||||
|
Loading…
Reference in New Issue
Block a user