Added min_price, max_price to PriceFilter
This commit is contained in:
parent
091285ba43
commit
14eab9be04
@ -66,7 +66,7 @@
|
|||||||
},
|
},
|
||||||
{"method": "AgeFilter", "min_days_listed": 10},
|
{"method": "AgeFilter", "min_days_listed": 10},
|
||||||
{"method": "PrecisionFilter"},
|
{"method": "PrecisionFilter"},
|
||||||
{"method": "PriceFilter", "low_price_ratio": 0.01},
|
{"method": "PriceFilter", "low_price_ratio": 0.01, "min_price": 0.00000010},
|
||||||
{"method": "SpreadFilter", "max_spread_ratio": 0.005}
|
{"method": "SpreadFilter", "max_spread_ratio": 0.005}
|
||||||
],
|
],
|
||||||
"exchange": {
|
"exchange": {
|
||||||
|
@ -662,16 +662,25 @@ Filters low-value coins which would not allow setting stoplosses.
|
|||||||
|
|
||||||
#### PriceFilter
|
#### PriceFilter
|
||||||
|
|
||||||
The `PriceFilter` allows filtering of pairs by price.
|
The `PriceFilter` allows filtering of pairs by price. Currently the following price filters are supported:
|
||||||
|
* `min_price`
|
||||||
|
* `max_price`
|
||||||
|
* `low_price_ratio`
|
||||||
|
|
||||||
Currently, only `low_price_ratio` setting is implemented, where a raise of 1 price unit (pip) is below the `low_price_ratio` ratio.
|
The `min_price` setting removes pairs where the price is below the specified price. This is useful if you wish to avoid trading very low-priced pairs.
|
||||||
|
This option is disabled by default, and will only apply if set to <> 0.
|
||||||
|
|
||||||
|
The `max_price` setting removes pairs where the price is above the specified price. This is useful if you wish to trade only low-priced pairs.
|
||||||
|
This option is disabled by default, and will only apply if set to <> 0.
|
||||||
|
|
||||||
|
The `low_price_ratio` setting removes pairs where a raise of 1 price unit (pip) is above the `low_price_ratio` ratio.
|
||||||
This option is disabled by default, and will only apply if set to <> 0.
|
This option is disabled by default, and will only apply if set to <> 0.
|
||||||
|
|
||||||
Calculation example:
|
Calculation example:
|
||||||
|
|
||||||
Min price precision is 8 decimals. If price is 0.00000011 - one step would be 0.00000012 - which is almost 10% higher than the previous value.
|
Min price precision is 8 decimals. If price is 0.00000011 - one step would be 0.00000012 - which is almost 10% higher than the previous value.
|
||||||
|
|
||||||
These pairs are dangerous since it may be impossible to place the desired stoploss - and often result in high losses. Here is what the PriceFilters takes over.
|
These pairs are dangerous since it may be impossible to place the desired stoploss - and often result in high losses.
|
||||||
|
|
||||||
#### ShuffleFilter
|
#### ShuffleFilter
|
||||||
|
|
||||||
|
@ -18,7 +18,11 @@ class PriceFilter(IPairList):
|
|||||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||||
|
|
||||||
self._low_price_ratio = pairlistconfig.get('low_price_ratio', 0)
|
self._low_price_ratio = pairlistconfig.get('low_price_ratio', 0)
|
||||||
self._enabled = self._low_price_ratio != 0
|
self._min_price = pairlistconfig.get('min_price', 0)
|
||||||
|
self._max_price = pairlistconfig.get('max_price', 0)
|
||||||
|
self._enabled = (self._low_price_ratio != 0) or \
|
||||||
|
(self._min_price != 0) or \
|
||||||
|
(self._max_price != 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def needstickers(self) -> bool:
|
def needstickers(self) -> bool:
|
||||||
@ -33,7 +37,18 @@ class PriceFilter(IPairList):
|
|||||||
"""
|
"""
|
||||||
Short whitelist method description - used for startup-messages
|
Short whitelist method description - used for startup-messages
|
||||||
"""
|
"""
|
||||||
return f"{self.name} - Filtering pairs priced below {self._low_price_ratio * 100}%."
|
active_price_filters = []
|
||||||
|
if self._low_price_ratio != 0:
|
||||||
|
active_price_filters.append(f"below {self._low_price_ratio * 100}%")
|
||||||
|
if self._min_price != 0:
|
||||||
|
active_price_filters.append(f"below {self._min_price:.8f}")
|
||||||
|
if self._max_price != 0:
|
||||||
|
active_price_filters.append(f"above {self._max_price:.8f}")
|
||||||
|
|
||||||
|
if len(active_price_filters):
|
||||||
|
return f"{self.name} - Filtering pairs priced {' or '.join(active_price_filters)}."
|
||||||
|
|
||||||
|
return f"{self.name} - No price filters configured."
|
||||||
|
|
||||||
def _validate_pair(self, ticker) -> bool:
|
def _validate_pair(self, ticker) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -46,10 +61,28 @@ class PriceFilter(IPairList):
|
|||||||
f"Removed {ticker['symbol']} from whitelist, because "
|
f"Removed {ticker['symbol']} from whitelist, because "
|
||||||
"ticker['last'] is empty (Usually no trade in the last 24h).")
|
"ticker['last'] is empty (Usually no trade in the last 24h).")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Perform low_price_ratio check.
|
||||||
|
if self._low_price_ratio != 0:
|
||||||
compare = self._exchange.price_get_one_pip(ticker['symbol'], ticker['last'])
|
compare = self._exchange.price_get_one_pip(ticker['symbol'], ticker['last'])
|
||||||
changeperc = compare / ticker['last']
|
changeperc = compare / ticker['last']
|
||||||
if changeperc > self._low_price_ratio:
|
if changeperc > self._low_price_ratio:
|
||||||
self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, "
|
self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, "
|
||||||
f"because 1 unit is {changeperc * 100:.3f}%")
|
f"because 1 unit is {changeperc * 100:.3f}%")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Perform min_price check.
|
||||||
|
if self._min_price != 0:
|
||||||
|
if ticker['last'] < self._min_price:
|
||||||
|
self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, "
|
||||||
|
f"because last price < {self._min_price:.8f}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Perform max_price check.
|
||||||
|
if self._max_price != 0:
|
||||||
|
if ticker['last'] > self._max_price:
|
||||||
|
self.log_on_refresh(logger.info, f"Removed {ticker['symbol']} from whitelist, "
|
||||||
|
f"because last price > {self._max_price:.8f}")
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -275,11 +275,16 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
|
|||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
|
||||||
{"method": "PriceFilter", "low_price_ratio": 0.03}],
|
{"method": "PriceFilter", "low_price_ratio": 0.03}],
|
||||||
"USDT", ['ETH/USDT', 'NANO/USDT']),
|
"USDT", ['ETH/USDT', 'NANO/USDT']),
|
||||||
# Hot is removed by precision_filter, Fuel by low_price_filter.
|
# Hot is removed by precision_filter, Fuel by low_price_ratio, Ripple by min_price.
|
||||||
([{"method": "VolumePairList", "number_assets": 6, "sort_key": "quoteVolume"},
|
([{"method": "VolumePairList", "number_assets": 6, "sort_key": "quoteVolume"},
|
||||||
{"method": "PrecisionFilter"},
|
{"method": "PrecisionFilter"},
|
||||||
{"method": "PriceFilter", "low_price_ratio": 0.02}],
|
{"method": "PriceFilter", "low_price_ratio": 0.02, "min_price": 0.01}],
|
||||||
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
|
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC']),
|
||||||
|
# Hot is removed by precision_filter, Fuel by low_price_ratio, Ethereum by max_price.
|
||||||
|
([{"method": "VolumePairList", "number_assets": 6, "sort_key": "quoteVolume"},
|
||||||
|
{"method": "PrecisionFilter"},
|
||||||
|
{"method": "PriceFilter", "low_price_ratio": 0.02, "max_price": 0.05}],
|
||||||
|
"BTC", ['TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
|
||||||
# HOT and XRP are removed because below 1250 quoteVolume
|
# HOT and XRP are removed because below 1250 quoteVolume
|
||||||
([{"method": "VolumePairList", "number_assets": 5,
|
([{"method": "VolumePairList", "number_assets": 5,
|
||||||
"sort_key": "quoteVolume", "min_value": 1250}],
|
"sort_key": "quoteVolume", "min_value": 1250}],
|
||||||
@ -319,7 +324,7 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
|
|||||||
"BTC", 'filter_at_the_beginning'), # OperationalException expected
|
"BTC", 'filter_at_the_beginning'), # OperationalException expected
|
||||||
# PriceFilter after StaticPairList
|
# PriceFilter after StaticPairList
|
||||||
([{"method": "StaticPairList"},
|
([{"method": "StaticPairList"},
|
||||||
{"method": "PriceFilter", "low_price_ratio": 0.02}],
|
{"method": "PriceFilter", "low_price_ratio": 0.02, "min_price": 0.000001, "max_price": 0.1}],
|
||||||
"BTC", ['ETH/BTC', 'TKN/BTC']),
|
"BTC", ['ETH/BTC', 'TKN/BTC']),
|
||||||
# PriceFilter only
|
# PriceFilter only
|
||||||
([{"method": "PriceFilter", "low_price_ratio": 0.02}],
|
([{"method": "PriceFilter", "low_price_ratio": 0.02}],
|
||||||
@ -396,6 +401,10 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
|
|||||||
r'would be <= stop limit.*', caplog)
|
r'would be <= stop limit.*', caplog)
|
||||||
if pairlist['method'] == 'PriceFilter' and whitelist_result:
|
if pairlist['method'] == 'PriceFilter' and whitelist_result:
|
||||||
assert (log_has_re(r'^Removed .* from whitelist, because 1 unit is .*%$', caplog) or
|
assert (log_has_re(r'^Removed .* from whitelist, because 1 unit is .*%$', caplog) or
|
||||||
|
log_has_re(r'^Removed .* from whitelist, '
|
||||||
|
r'because last price < .*%$', caplog) or
|
||||||
|
log_has_re(r'^Removed .* from whitelist, '
|
||||||
|
r'because last price > .*%$', caplog) or
|
||||||
log_has_re(r"^Removed .* from whitelist, because ticker\['last'\] "
|
log_has_re(r"^Removed .* from whitelist, because ticker\['last'\] "
|
||||||
r"is empty.*", caplog))
|
r"is empty.*", caplog))
|
||||||
if pairlist['method'] == 'VolumePairList':
|
if pairlist['method'] == 'VolumePairList':
|
||||||
|
Loading…
Reference in New Issue
Block a user