Simplify timerange handling

This commit is contained in:
Matthias 2022-11-10 18:26:14 +01:00
parent 57313dd961
commit 0f9c5f8d41
6 changed files with 55 additions and 33 deletions

View File

@ -8,6 +8,7 @@ from typing import Optional
import arrow import arrow
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
@ -41,6 +42,40 @@ class TimeRange:
return datetime.fromtimestamp(self.stopts, tz=timezone.utc) return datetime.fromtimestamp(self.stopts, tz=timezone.utc)
return None return None
@property
def timerange_str(self) -> str:
"""
Returns a string representation of the timerange as used by parse_timerange.
Follows the format yyyymmdd-yyyymmdd - leaving out the parts that are not set.
"""
start = ''
stop = ''
if startdt := self.startdt:
start = startdt.strftime('%Y%m%d')
if stopdt := self.stopdt:
stop = stopdt.strftime('%Y%m%d')
return f"{start}-{stop}"
@property
def start_fmt(self) -> str:
"""
Returns a string representation of the start date
"""
val = 'unbounded'
if (startdt := self.startdt) is not None:
val = startdt.strftime(DATETIME_PRINT_FORMAT)
return val
@property
def stop_fmt(self) -> str:
"""
Returns a string representation of the stop date
"""
val = 'unbounded'
if (stopdt := self.stopdt) is not None:
val = stopdt.strftime(DATETIME_PRINT_FORMAT)
return val
def __eq__(self, other): def __eq__(self, other):
"""Override the default Equals behavior""" """Override the default Equals behavior"""
return (self.starttype == other.starttype and self.stoptype == other.stoptype return (self.starttype == other.starttype and self.stoptype == other.stoptype

View File

@ -432,9 +432,7 @@ class FreqaiDataKitchen:
timerange_train.stopts = timerange_train.startts + train_period_days timerange_train.stopts = timerange_train.startts + train_period_days
first = False first = False
start = timerange_train.startdt tr_training_list.append(timerange_train.timerange_str)
stop = timerange_train.stopdt
tr_training_list.append(start.strftime("%Y%m%d") + "-" + stop.strftime("%Y%m%d"))
tr_training_list_timerange.append(copy.deepcopy(timerange_train)) tr_training_list_timerange.append(copy.deepcopy(timerange_train))
# associated backtest period # associated backtest period
@ -446,9 +444,7 @@ class FreqaiDataKitchen:
if timerange_backtest.stopts > config_timerange.stopts: if timerange_backtest.stopts > config_timerange.stopts:
timerange_backtest.stopts = config_timerange.stopts timerange_backtest.stopts = config_timerange.stopts
start = timerange_backtest.startdt tr_backtesting_list.append(timerange_backtest.timerange_str)
stop = timerange_backtest.stopdt
tr_backtesting_list.append(start.strftime("%Y%m%d") + "-" + stop.strftime("%Y%m%d"))
tr_backtesting_list_timerange.append(copy.deepcopy(timerange_backtest)) tr_backtesting_list_timerange.append(copy.deepcopy(timerange_backtest))
# ensure we are predicting on exactly same amount of data as requested by user defined # ensure we are predicting on exactly same amount of data as requested by user defined
@ -1055,9 +1051,7 @@ class FreqaiDataKitchen:
backtest_timerange.startts = ( backtest_timerange.startts = (
backtest_timerange.startts - backtest_period_days * SECONDS_IN_DAY backtest_timerange.startts - backtest_period_days * SECONDS_IN_DAY
) )
start = backtest_timerange.startdt full_timerange = backtest_timerange.timerange_str
stop = backtest_timerange.stopdt
full_timerange = start.strftime("%Y%m%d") + "-" + stop.strftime("%Y%m%d")
config_path = Path(self.config["config_files"][0]) config_path = Path(self.config["config_files"][0])
if not self.full_path.is_dir(): if not self.full_path.is_dir():

View File

@ -13,7 +13,7 @@ from numpy.typing import NDArray
from pandas import DataFrame from pandas import DataFrame
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config from freqtrade.constants import Config
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_seconds from freqtrade.exchange import timeframe_to_seconds
@ -788,14 +788,8 @@ class IFreqaiModel(ABC):
:return: if the data exists or not :return: if the data exists or not
""" """
if self.config.get("freqai_backtest_live_models", False) and len(dataframe_backtest) == 0: if self.config.get("freqai_backtest_live_models", False) and len(dataframe_backtest) == 0:
tr_backtest_startts_str = datetime.fromtimestamp( logger.info(f"No data found for pair {pair} from "
tr_backtest.startts, f"from { tr_backtest.start_fmt} to {tr_backtest.stop_fmt}. "
tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)
tr_backtest_stopts_str = datetime.fromtimestamp(
tr_backtest.stopts,
tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)
logger.info(f"No data found for pair {pair} from {tr_backtest_startts_str} "
f" from {tr_backtest_startts_str} to {tr_backtest_stopts_str}. "
"Probably more than one training within the same candle period.") "Probably more than one training within the same candle period.")
return False return False
return True return True
@ -810,18 +804,11 @@ class IFreqaiModel(ABC):
:param pair: the current pair :param pair: the current pair
:param total_trains: total trains (total number of slides for the sliding window) :param total_trains: total trains (total number of slides for the sliding window)
""" """
tr_train_startts_str = datetime.fromtimestamp(
tr_train.startts,
tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)
tr_train_stopts_str = datetime.fromtimestamp(
tr_train.stopts,
tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)
if not self.config.get("freqai_backtest_live_models", False): if not self.config.get("freqai_backtest_live_models", False):
logger.info( logger.info(
f"Training {pair}, {self.pair_it}/{self.total_pairs} pairs" f"Training {pair}, {self.pair_it}/{self.total_pairs} pairs"
f" from {tr_train_startts_str} " f" from {tr_train.start_fmt} "
f"to {tr_train_stopts_str}, {train_it}/{total_trains} " f"to {tr_train.stop_fmt}, {train_it}/{total_trains} "
"trains" "trains"
) )
# Following methods which are overridden by user made prediction models. # Following methods which are overridden by user made prediction models.

View File

@ -230,5 +230,4 @@ def get_timerange_backtest_live_models(config: Config) -> str:
dk = FreqaiDataKitchen(config) dk = FreqaiDataKitchen(config)
models_path = dk.get_full_models_path(config) models_path = dk.get_full_models_path(config)
timerange, _ = dk.get_timerange_and_assets_end_dates_from_ready_models(models_path) timerange, _ = dk.get_timerange_and_assets_end_dates_from_ready_models(models_path)
tr = f"{timerange.startdt.strftime('%Y%m%d')}-{timerange.stopdt.strftime('%Y%m%d')}" return timerange.timerange_str
return tr

View File

@ -1286,8 +1286,7 @@ class Backtesting:
def _get_min_cached_backtest_date(self): def _get_min_cached_backtest_date(self):
min_backtest_date = None min_backtest_date = None
backtest_cache_age = self.config.get('backtest_cache', constants.BACKTEST_CACHE_DEFAULT) backtest_cache_age = self.config.get('backtest_cache', constants.BACKTEST_CACHE_DEFAULT)
if self.timerange.stopts == 0 or datetime.fromtimestamp( if self.timerange.stopts == 0 or self.timerange.stopdt > datetime.now(tz=timezone.utc):
self.timerange.stopts, tz=timezone.utc) > datetime.now(tz=timezone.utc):
logger.warning('Backtest result caching disabled due to use of open-ended timerange.') logger.warning('Backtest result caching disabled due to use of open-ended timerange.')
elif backtest_cache_age == 'day': elif backtest_cache_age == 'day':
min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(days=1) min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(days=1)

View File

@ -10,10 +10,17 @@ from freqtrade.exceptions import OperationalException
def test_parse_timerange_incorrect(): def test_parse_timerange_incorrect():
assert TimeRange('date', None, 1274486400, 0) == TimeRange.parse_timerange('20100522-') timerange = TimeRange.parse_timerange('20100522-')
assert TimeRange(None, 'date', 0, 1274486400) == TimeRange.parse_timerange('-20100522') assert TimeRange('date', None, 1274486400, 0) == timerange
assert timerange.timerange_str == '20100522-'
timerange = TimeRange.parse_timerange('-20100522')
assert TimeRange(None, 'date', 0, 1274486400) == timerange
assert timerange.timerange_str == '-20100522'
timerange = TimeRange.parse_timerange('20100522-20150730') timerange = TimeRange.parse_timerange('20100522-20150730')
assert timerange == TimeRange('date', 'date', 1274486400, 1438214400) assert timerange == TimeRange('date', 'date', 1274486400, 1438214400)
assert timerange.timerange_str == '20100522-20150730'
assert timerange.start_fmt == '2010-05-22 00:00:00'
assert timerange.stop_fmt == '2015-07-30 00:00:00'
# Added test for unix timestamp - BTC genesis date # Added test for unix timestamp - BTC genesis date
assert TimeRange('date', None, 1231006505, 0) == TimeRange.parse_timerange('1231006505-') assert TimeRange('date', None, 1231006505, 0) == TimeRange.parse_timerange('1231006505-')
@ -24,6 +31,7 @@ def test_parse_timerange_incorrect():
assert isinstance(timerange.stopdt, datetime) assert isinstance(timerange.stopdt, datetime)
assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=timezone.utc) assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=timezone.utc)
assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=timezone.utc) assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=timezone.utc)
assert timerange.timerange_str == '20090103-20090131'
timerange = TimeRange.parse_timerange('1231006505000-1233360000000') timerange = TimeRange.parse_timerange('1231006505000-1233360000000')
assert TimeRange('date', 'date', 1231006505, 1233360000) == timerange assert TimeRange('date', 'date', 1231006505, 1233360000) == timerange