Repository: freqtrade/freqtrade

Issue:
PerformanceFilter - last x mins  
Hi for the first time and thank you for this magnificent piece of software :) 

I just discovered PerformanceFilter for the pairlists. I am excited about it and I would like to share some thoughts about possible improvements to this. 

Idea is to give possibility of taking into account performance from last x mins, hours, days. It can happen that one of the pairs had bad streak in first hour losing f.e. -3% and other pairs winning 0.1%. Having 30 pairs and trading 10 best of them it would mean that for this pair it's impossible to move to top 10 before all other pairs will prove to be worse. It can actually never happen and this pair could be the best in 2nd hour.



Discussion:
i guess it could be added ... 

You'll need to be careful with your scenario though - assuming from the 30 pairs, 11 or 12 are "mostly" winners - you'll exclude most of the latter pairs "in principle" - just because they never got a chance to prove themselfs - so using this sorting method to reduce the number of pairs can be dangerous too.

I understand your concern. I was also thinking about it. My strategy is doing a lot of short trades on 1m timeframe and it is vulnerable to the local trends or price moving sideways. If all of my pairs are winning - I am happy enough :) 

Basically I think in case when this setting is not in place is even more risky that those pairs will never have chance to prove value. 

In my case I see it like this:
1. I would be using the sorting based on last 1hr performance.
2. If all the pairs have buy signal at the same time - best 10 will have priority. 
3. If not all from top 10 receive buy signal - pairs from the bottom have chance to prove themselves.
4. Pairs that didn't have chance to prove themselves in last period will have a chance over those losing in last period.

In my head it makes sense but maybe I am missing something :)


Resolving patch:
diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py
--- a/freqtrade/persistence/models.py
+++ b/freqtrade/persistence/models.py
@@ -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 @@ def total_open_trades_stakes() -> float:
         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()
diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py
--- a/freqtrade/plugins/pairlist/PerformanceFilter.py
+++ b/freqtrade/plugins/pairlist/PerformanceFilter.py
@@ -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 @@
 
 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 @@ def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
         """
         # 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)


Test patch:
diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py
--- a/tests/plugins/test_pairlist.py
+++ b/tests/plugins/test_pairlist.py
@@ -12,7 +12,8 @@
 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}]