2022-09-26 22:01:24 +00:00
|
|
|
"""
|
|
|
|
FreqAI generic functions
|
|
|
|
"""
|
2022-09-26 02:14:00 +00:00
|
|
|
import logging
|
2022-09-27 03:14:12 +00:00
|
|
|
from datetime import datetime, timedelta, timezone
|
2022-09-26 02:14:00 +00:00
|
|
|
from pathlib import Path
|
2022-09-26 22:08:25 +00:00
|
|
|
from typing import Any, Dict, Tuple
|
2022-09-26 02:14:00 +00:00
|
|
|
|
|
|
|
from freqtrade.configuration import TimeRange
|
|
|
|
from freqtrade.constants import Config
|
|
|
|
from freqtrade.exceptions import OperationalException
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
def get_full_model_path(config: Config) -> Path:
|
2022-09-26 22:01:24 +00:00
|
|
|
"""
|
|
|
|
Returns default FreqAI model path
|
|
|
|
:param config: Configuration dictionary
|
|
|
|
"""
|
2022-09-26 02:14:00 +00:00
|
|
|
freqai_config: Dict[str, Any] = config["freqai"]
|
|
|
|
return Path(
|
|
|
|
config["user_data_dir"] / "models" / str(freqai_config.get("identifier"))
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-09-26 22:21:53 +00:00
|
|
|
def get_timerange_from_ready_models(models_path: Path) -> Tuple[TimeRange, str, Dict[str, Any]]:
|
2022-09-26 22:01:24 +00:00
|
|
|
"""
|
|
|
|
Returns timerange information based on a FreqAI model directory
|
|
|
|
:param models_path: FreqAI model path
|
|
|
|
|
|
|
|
:returns: a Tuple with (backtesting_timerange: Timerange calculated from directory,
|
|
|
|
backtesting_string_timerange: str timerange calculated from
|
|
|
|
directory (format example '20020822-20220830'), \
|
|
|
|
pairs_end_dates: Dict with pair and model end training dates info)
|
|
|
|
"""
|
2022-09-26 02:14:00 +00:00
|
|
|
all_models_end_dates = []
|
|
|
|
pairs_end_dates: Dict[str, Any] = {}
|
2022-09-26 22:01:24 +00:00
|
|
|
if not models_path.is_dir():
|
|
|
|
raise OperationalException(
|
|
|
|
'Model folders not found. Saved models are required '
|
|
|
|
'to run backtest with the freqai-backtest-live-models option'
|
|
|
|
)
|
2022-09-26 02:14:00 +00:00
|
|
|
for model_dir in models_path.iterdir():
|
|
|
|
if str(model_dir.name).startswith("sub-train"):
|
|
|
|
model_end_date = int(model_dir.name.split("_")[1])
|
|
|
|
pair = model_dir.name.split("_")[0].replace("sub-train-", "")
|
|
|
|
model_file_name = (
|
|
|
|
f"cb_{str(model_dir.name).replace('sub-train-', '').lower()}"
|
|
|
|
"_model.joblib"
|
|
|
|
)
|
|
|
|
|
|
|
|
model_path_file = Path(model_dir / model_file_name)
|
|
|
|
if model_path_file.is_file():
|
|
|
|
if pair not in pairs_end_dates:
|
|
|
|
pairs_end_dates[pair] = []
|
|
|
|
|
|
|
|
pairs_end_dates[pair].append({
|
|
|
|
"model_end_date": model_end_date,
|
|
|
|
"model_path_file": model_path_file,
|
|
|
|
"model_dir": model_dir
|
|
|
|
})
|
|
|
|
|
|
|
|
if model_end_date not in all_models_end_dates:
|
|
|
|
all_models_end_dates.append(model_end_date)
|
|
|
|
|
|
|
|
if len(all_models_end_dates) == 0:
|
|
|
|
raise OperationalException(
|
|
|
|
'At least 1 saved model is required to '
|
2022-09-26 22:01:24 +00:00
|
|
|
'run backtest with the freqai-backtest-live-models option'
|
2022-09-26 02:14:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if len(all_models_end_dates) == 1:
|
|
|
|
logger.warning(
|
|
|
|
"Only 1 model was found. Backtesting will run with the "
|
|
|
|
"timerange from the end of the training date to the current date"
|
|
|
|
)
|
|
|
|
|
|
|
|
finish_timestamp = int(datetime.now(tz=timezone.utc).timestamp())
|
|
|
|
if len(all_models_end_dates) > 1:
|
|
|
|
# After last model end date, use the same period from previous model
|
|
|
|
# to finish the backtest
|
|
|
|
all_models_end_dates.sort(reverse=True)
|
|
|
|
finish_timestamp = all_models_end_dates[0] + \
|
|
|
|
(all_models_end_dates[0] - all_models_end_dates[1])
|
|
|
|
|
|
|
|
all_models_end_dates.append(finish_timestamp)
|
|
|
|
all_models_end_dates.sort()
|
|
|
|
start = datetime.fromtimestamp(min(all_models_end_dates), tz=timezone.utc)
|
|
|
|
stop = datetime.fromtimestamp(max(all_models_end_dates), tz=timezone.utc)
|
2022-09-27 03:14:12 +00:00
|
|
|
end_date_string_timerange = stop
|
|
|
|
if (
|
|
|
|
finish_timestamp < int(datetime.now(tz=timezone.utc).timestamp()) and
|
|
|
|
datetime.now(tz=timezone.utc).strftime('%Y%m%d') != stop.strftime('%Y%m%d')
|
|
|
|
):
|
|
|
|
# add 1 day to string timerange to ensure BT module will load all dataframe data
|
|
|
|
end_date_string_timerange = stop + timedelta(days=1)
|
|
|
|
|
|
|
|
backtesting_string_timerange = (
|
|
|
|
f"{start.strftime('%Y%m%d')}-{end_date_string_timerange.strftime('%Y%m%d')}"
|
|
|
|
)
|
2022-09-26 02:14:00 +00:00
|
|
|
backtesting_timerange = TimeRange(
|
|
|
|
'date', 'date', min(all_models_end_dates), max(all_models_end_dates)
|
|
|
|
)
|
|
|
|
return backtesting_timerange, backtesting_string_timerange, pairs_end_dates
|