Initial step towards implementing proposed code

This commit is contained in:
Leif Segen 2020-11-27 21:24:40 -06:00
parent 46ec6f498c
commit 7cbd89657f
3 changed files with 83 additions and 2 deletions

View File

@ -25,7 +25,7 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
'AgeFilter', 'PrecisionFilter', 'PriceFilter',
'ShuffleFilter', 'SpreadFilter']
'ShuffleFilter', 'SpreadFilter', 'PerformanceFilter']
AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5']
DRY_RUN_WALLET = 1000
DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S'

View File

@ -0,0 +1,61 @@
"""
Performance pair list filter
"""
import logging
import random
from typing import Any, Dict, List
import pandas as pd
from pandas import DataFrame, Series
from freqtrade.pairlist.IPairList import IPairList
from freqtrade.persistence import Trade
from datetime import timedelta, datetime, timezone
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)
@property
def needstickers(self) -> bool:
"""
Boolean property defining if tickers are necessary.
If no Pairlist requries tickers, an empty List is passed
as tickers argument to filter_pairlist
"""
return False
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
"""
return f"{self.name} - Sorting pairs by performance."
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
"""
Filters and sorts pairlist and returns the whitelist again.
Called on each bot iteration - please use internal caching if necessary
:param pairlist: pairlist to filter or sort
:param tickers: Tickers (from exchange.get_tickers()). May be cached.
:return: new whitelist
"""
# Get the trading performance for pairs from database
perf = pd.DataFrame(Trade.get_overall_performance())
# update pairlist with values from performance dataframe
# set initial value for pairs with no trades to 0
# and sort the list using performance and count
list_df = pd.DataFrame({'pair':pairlist})
sorted_df = list_df.join(perf.set_index('pair'), on='pair')\
.fillna(0).sort_values(by=['profit', 'count'], ascending=False)
pairlist = sorted_df['pair'].tolist()
return pairlist

View File

@ -246,7 +246,7 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
{"method": "PrecisionFilter"},
{"method": "PriceFilter", "low_price_ratio": 0.03},
{"method": "SpreadFilter", "max_spread_ratio": 0.005},
{"method": "ShuffleFilter"}],
{"method": "ShuffleFilter"}, {"method": "PerformanceFilter"}],
"ETH", []),
# AgeFilter and VolumePairList (require 2 days only, all should pass age test)
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
@ -302,6 +302,18 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "ShuffleFilter"}],
"USDT", 3), # whitelist_result is integer -- check only length of randomized pairlist
# PerformanceFilter
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "PerformanceFilter", "seed": 77}],
"USDT", ['ADADOUBLE/USDT', 'ETH/USDT', 'NANO/USDT', 'ADAHALF/USDT']),
# PerformanceFilter, other seed
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "PerformanceFilter", "seed": 42}],
"USDT", ['ADAHALF/USDT', 'NANO/USDT', 'ADADOUBLE/USDT', 'ETH/USDT']),
# PerformanceFilter, no seed
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "PerformanceFilter"}],
"USDT", 3), # whitelist_result is integer -- check only length of randomized pairlist
# AgeFilter only
([{"method": "AgeFilter", "min_days_listed": 2}],
"BTC", 'filter_at_the_beginning'), # OperationalException expected
@ -326,6 +338,13 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
# ShuffleFilter only
([{"method": "ShuffleFilter", "seed": 42}],
"BTC", 'filter_at_the_beginning'), # OperationalException expected
# PrecisionFilter after StaticPairList
([{"method": "StaticPairList"},
{"method": "PrecisionFilter", "seed": 42}],
"BTC", ['TKN/BTC', 'ETH/BTC', 'HOT/BTC']),
# PrecisionFilter only
([{"method": "PrecisionFilter", "seed": 42}],
"BTC", 'filter_at_the_beginning'), # OperationalException expected
# SpreadFilter after StaticPairList
([{"method": "StaticPairList"},
{"method": "SpreadFilter", "max_spread_ratio": 0.005}],
@ -379,6 +398,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
assert isinstance(whitelist, list)
# Verify length of pairlist matches (used for ShuffleFilter without seed)
# TBD if this applies to PerformanceFilter
if type(whitelist_result) is list:
assert whitelist == whitelist_result
else: