Merge pull request #4636 from freqtrade/fix/4298
Allow edge to use dynamic pairlists
This commit is contained in:
		| @@ -3,7 +3,7 @@ | |||||||
| The `Edge Positioning` module uses probability to calculate your win rate and risk reward ratio. It will use these statistics to control your strategy trade entry points, position size and, stoploss. | The `Edge Positioning` module uses probability to calculate your win rate and risk reward ratio. It will use these statistics to control your strategy trade entry points, position size and, stoploss. | ||||||
|  |  | ||||||
| !!! Warning | !!! Warning | ||||||
|     `Edge positioning` is not compatible with dynamic (volume-based) whitelist. |     WHen using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data. | ||||||
|  |  | ||||||
| !!! Note | !!! Note | ||||||
|     `Edge Positioning` only considers *its own* buy/sell/stoploss signals. It ignores the stoploss, trailing stoploss, and ROI settings in the strategy configuration file. |     `Edge Positioning` only considers *its own* buy/sell/stoploss signals. It ignores the stoploss, trailing stoploss, and ROI settings in the strategy configuration file. | ||||||
|   | |||||||
| @@ -149,11 +149,6 @@ def _validate_edge(conf: Dict[str, Any]) -> None: | |||||||
|     if not conf.get('edge', {}).get('enabled'): |     if not conf.get('edge', {}).get('enabled'): | ||||||
|         return |         return | ||||||
|  |  | ||||||
|     if conf.get('pairlist', {}).get('method') == 'VolumePairList': |  | ||||||
|         raise OperationalException( |  | ||||||
|             "Edge and VolumePairList are incompatible, " |  | ||||||
|             "Edge will override whatever pairs VolumePairlist selects." |  | ||||||
|         ) |  | ||||||
|     if not conf.get('ask_strategy', {}).get('use_sell_signal', True): |     if not conf.get('ask_strategy', {}).get('use_sell_signal', True): | ||||||
|         raise OperationalException( |         raise OperationalException( | ||||||
|             "Edge requires `use_sell_signal` to be True, otherwise no sells will happen." |             "Edge requires `use_sell_signal` to be True, otherwise no sells will happen." | ||||||
|   | |||||||
| @@ -84,9 +84,8 @@ class Edge: | |||||||
|             self.fee = self.exchange.get_fee(symbol=expand_pairlist( |             self.fee = self.exchange.get_fee(symbol=expand_pairlist( | ||||||
|                 self.config['exchange']['pair_whitelist'], list(self.exchange.markets))[0]) |                 self.config['exchange']['pair_whitelist'], list(self.exchange.markets))[0]) | ||||||
|  |  | ||||||
|     def calculate(self) -> bool: |     def calculate(self, pairs: List[str]) -> bool: | ||||||
|         pairs = expand_pairlist(self.config['exchange']['pair_whitelist'], |  | ||||||
|                                 list(self.exchange.markets)) |  | ||||||
|         heartbeat = self.edge_config.get('process_throttle_secs') |         heartbeat = self.edge_config.get('process_throttle_secs') | ||||||
|  |  | ||||||
|         if (self._last_updated > 0) and ( |         if (self._last_updated > 0) and ( | ||||||
|   | |||||||
| @@ -225,7 +225,7 @@ class FreqtradeBot(LoggingMixin): | |||||||
|  |  | ||||||
|         # Calculating Edge positioning |         # Calculating Edge positioning | ||||||
|         if self.edge: |         if self.edge: | ||||||
|             self.edge.calculate() |             self.edge.calculate(_whitelist) | ||||||
|             _whitelist = self.edge.adjust(_whitelist) |             _whitelist = self.edge.adjust(_whitelist) | ||||||
|  |  | ||||||
|         if trades: |         if trades: | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ class EdgeCli: | |||||||
|             'timerange') is None else str(self.config.get('timerange'))) |             'timerange') is None else str(self.config.get('timerange'))) | ||||||
|  |  | ||||||
|     def start(self) -> None: |     def start(self) -> None: | ||||||
|         result = self.edge.calculate() |         result = self.edge.calculate(self.config['exchange']['pair_whitelist']) | ||||||
|         if result: |         if result: | ||||||
|             print('')  # blank line for readability |             print('')  # blank line for readability | ||||||
|             print(generate_edge_table(self.edge._cached_pairs)) |             print(generate_edge_table(self.edge._cached_pairs)) | ||||||
|   | |||||||
| @@ -266,7 +266,7 @@ def test_edge_heartbeat_calculate(mocker, edge_conf): | |||||||
|     # should not recalculate if heartbeat not reached |     # should not recalculate if heartbeat not reached | ||||||
|     edge._last_updated = arrow.utcnow().int_timestamp - heartbeat + 1 |     edge._last_updated = arrow.utcnow().int_timestamp - heartbeat + 1 | ||||||
|  |  | ||||||
|     assert edge.calculate() is False |     assert edge.calculate(edge_conf['exchange']['pair_whitelist']) is False | ||||||
|  |  | ||||||
|  |  | ||||||
| def mocked_load_data(datadir, pairs=[], timeframe='0m', | def mocked_load_data(datadir, pairs=[], timeframe='0m', | ||||||
| @@ -310,7 +310,7 @@ def test_edge_process_downloaded_data(mocker, edge_conf): | |||||||
|     mocker.patch('freqtrade.edge.edge_positioning.load_data', mocked_load_data) |     mocker.patch('freqtrade.edge.edge_positioning.load_data', mocked_load_data) | ||||||
|     edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy) |     edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy) | ||||||
|  |  | ||||||
|     assert edge.calculate() |     assert edge.calculate(edge_conf['exchange']['pair_whitelist']) | ||||||
|     assert len(edge._cached_pairs) == 2 |     assert len(edge._cached_pairs) == 2 | ||||||
|     assert edge._last_updated <= arrow.utcnow().int_timestamp + 2 |     assert edge._last_updated <= arrow.utcnow().int_timestamp + 2 | ||||||
|  |  | ||||||
| @@ -322,7 +322,7 @@ def test_edge_process_no_data(mocker, edge_conf, caplog): | |||||||
|     mocker.patch('freqtrade.edge.edge_positioning.load_data', MagicMock(return_value={})) |     mocker.patch('freqtrade.edge.edge_positioning.load_data', MagicMock(return_value={})) | ||||||
|     edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy) |     edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy) | ||||||
|  |  | ||||||
|     assert not edge.calculate() |     assert not edge.calculate(edge_conf['exchange']['pair_whitelist']) | ||||||
|     assert len(edge._cached_pairs) == 0 |     assert len(edge._cached_pairs) == 0 | ||||||
|     assert log_has("No data found. Edge is stopped ...", caplog) |     assert log_has("No data found. Edge is stopped ...", caplog) | ||||||
|     assert edge._last_updated == 0 |     assert edge._last_updated == 0 | ||||||
| @@ -337,7 +337,7 @@ def test_edge_process_no_trades(mocker, edge_conf, caplog): | |||||||
|     mocker.patch('freqtrade.edge.Edge._find_trades_for_stoploss_range', MagicMock(return_value=[])) |     mocker.patch('freqtrade.edge.Edge._find_trades_for_stoploss_range', MagicMock(return_value=[])) | ||||||
|     edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy) |     edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy) | ||||||
|  |  | ||||||
|     assert not edge.calculate() |     assert not edge.calculate(edge_conf['exchange']['pair_whitelist']) | ||||||
|     assert len(edge._cached_pairs) == 0 |     assert len(edge._cached_pairs) == 0 | ||||||
|     assert log_has("No trades found.", caplog) |     assert log_has("No trades found.", caplog) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -860,22 +860,6 @@ def test_validate_tsl(default_conf): | |||||||
|         validate_config_consistency(default_conf) |         validate_config_consistency(default_conf) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_validate_edge(edge_conf): |  | ||||||
|     edge_conf.update({"pairlist": { |  | ||||||
|         "method": "VolumePairList", |  | ||||||
|     }}) |  | ||||||
|  |  | ||||||
|     with pytest.raises(OperationalException, |  | ||||||
|                        match="Edge and VolumePairList are incompatible, " |  | ||||||
|                        "Edge will override whatever pairs VolumePairlist selects."): |  | ||||||
|         validate_config_consistency(edge_conf) |  | ||||||
|  |  | ||||||
|     edge_conf.update({"pairlist": { |  | ||||||
|         "method": "StaticPairList", |  | ||||||
|     }}) |  | ||||||
|     validate_config_consistency(edge_conf) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_validate_edge2(edge_conf): | def test_validate_edge2(edge_conf): | ||||||
|     edge_conf.update({"ask_strategy": { |     edge_conf.update({"ask_strategy": { | ||||||
|         "use_sell_signal": True, |         "use_sell_signal": True, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user