Merge pull request #5582 from sergeykhliustin/develop
Added days parameter to PerformanceFilter
This commit is contained in:
		| @@ -165,6 +165,7 @@ Example to remove the first 10 pairs from the pairlist: | ||||
|  | ||||
| ```json | ||||
| "pairlists": [ | ||||
|     // ... | ||||
|     { | ||||
|         "method": "OffsetFilter", | ||||
|         "offset": 10 | ||||
| @@ -190,6 +191,19 @@ Sorts pairs by past trade performance, as follows: | ||||
|  | ||||
| Trade count is used as a tie breaker. | ||||
|  | ||||
| You can use the `minutes` parameter to only consider performance of the past X minutes (rolling window). | ||||
| Not defining this parameter (or setting it to 0) will use all-time performance. | ||||
|  | ||||
| ```json | ||||
| "pairlists": [ | ||||
|     // ... | ||||
|     { | ||||
|         "method": "PerformanceFilter", | ||||
|         "minutes": 1440  // rolling 24h | ||||
|     } | ||||
| ], | ||||
| ``` | ||||
|  | ||||
| !!! Note | ||||
|     `PerformanceFilter` does not support backtesting mode. | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| This module contains the class to persist trades into SQLite | ||||
| """ | ||||
| import logging | ||||
| from datetime import datetime, timezone | ||||
| from datetime import datetime, timedelta, timezone | ||||
| from decimal import Decimal | ||||
| from typing import Any, Dict, List, Optional | ||||
|  | ||||
| @@ -832,17 +832,21 @@ class Trade(_DECL_BASE, LocalTrade): | ||||
|         return total_open_stake_amount or 0 | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_overall_performance() -> List[Dict[str, Any]]: | ||||
|     def get_overall_performance(minutes=None) -> List[Dict[str, Any]]: | ||||
|         """ | ||||
|         Returns List of dicts containing all Trades, including profit and trade count | ||||
|         NOTE: Not supported in Backtesting. | ||||
|         """ | ||||
|         filters = [Trade.is_open.is_(False)] | ||||
|         if minutes: | ||||
|             start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) | ||||
|             filters.append(Trade.close_date >= start_date) | ||||
|         pair_rates = Trade.query.with_entities( | ||||
|             Trade.pair, | ||||
|             func.sum(Trade.close_profit).label('profit_sum'), | ||||
|             func.sum(Trade.close_profit_abs).label('profit_sum_abs'), | ||||
|             func.count(Trade.pair).label('count') | ||||
|         ).filter(Trade.is_open.is_(False))\ | ||||
|         ).filter(*filters)\ | ||||
|             .group_by(Trade.pair) \ | ||||
|             .order_by(desc('profit_sum_abs')) \ | ||||
|             .all() | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| Performance pair list filter | ||||
| """ | ||||
| import logging | ||||
| from typing import Dict, List | ||||
| from typing import Any, Dict, List | ||||
|  | ||||
| import pandas as pd | ||||
|  | ||||
| @@ -15,6 +15,13 @@ logger = logging.getLogger(__name__) | ||||
|  | ||||
| class PerformanceFilter(IPairList): | ||||
|  | ||||
|     def __init__(self, exchange, pairlistmanager, | ||||
|                  config: Dict[str, Any], pairlistconfig: Dict[str, Any], | ||||
|                  pairlist_pos: int) -> None: | ||||
|         super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) | ||||
|  | ||||
|         self._minutes = pairlistconfig.get('minutes', 0) | ||||
|  | ||||
|     @property | ||||
|     def needstickers(self) -> bool: | ||||
|         """ | ||||
| @@ -40,7 +47,7 @@ class PerformanceFilter(IPairList): | ||||
|         """ | ||||
|         # Get the trading performance for pairs from database | ||||
|         try: | ||||
|             performance = pd.DataFrame(Trade.get_overall_performance()) | ||||
|             performance = pd.DataFrame(Trade.get_overall_performance(self._minutes)) | ||||
|         except AttributeError: | ||||
|             # Performancefilter does not work in backtesting. | ||||
|             self.log_once("PerformanceFilter is not available in this mode.", logger.warning) | ||||
|   | ||||
| @@ -12,7 +12,8 @@ from freqtrade.persistence import Trade | ||||
| from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist | ||||
| from freqtrade.plugins.pairlistmanager import PairListManager | ||||
| from freqtrade.resolvers import PairListResolver | ||||
| from tests.conftest import get_patched_exchange, get_patched_freqtradebot, log_has, log_has_re | ||||
| from tests.conftest import (create_mock_trades, get_patched_exchange, get_patched_freqtradebot, | ||||
|                             log_has, log_has_re) | ||||
|  | ||||
|  | ||||
| @pytest.fixture(scope="function") | ||||
| @@ -663,6 +664,31 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None: | ||||
|     assert log_has("PerformanceFilter is not available in this mode.", caplog) | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures("init_persistence") | ||||
| def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee) -> None: | ||||
|     whitelist_conf['exchange']['pair_whitelist'].append('XRP/BTC') | ||||
|     whitelist_conf['pairlists'] = [ | ||||
|         {"method": "StaticPairList"}, | ||||
|         {"method": "PerformanceFilter", "minutes": 60} | ||||
|     ] | ||||
|     mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) | ||||
|     exchange = get_patched_exchange(mocker, whitelist_conf) | ||||
|     pm = PairListManager(exchange, whitelist_conf) | ||||
|     pm.refresh_pairlist() | ||||
|  | ||||
|     assert pm.whitelist == ['ETH/BTC', 'TKN/BTC', 'XRP/BTC'] | ||||
|  | ||||
|     with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: | ||||
|         create_mock_trades(fee) | ||||
|         pm.refresh_pairlist() | ||||
|         assert pm.whitelist == ['XRP/BTC', 'ETH/BTC', 'TKN/BTC'] | ||||
|  | ||||
|         # Move to "outside" of lookback window, so original sorting is restored. | ||||
|         t.move_to("2021-09-01 07:00:00 +00:00") | ||||
|         pm.refresh_pairlist() | ||||
|         assert pm.whitelist == ['ETH/BTC', 'TKN/BTC', 'XRP/BTC'] | ||||
|  | ||||
|  | ||||
| def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None: | ||||
|     default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}] | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user