merge develop into feat/freqai-rl-dev
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
||||
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@@ -154,6 +155,23 @@ def test_jsondatahandler_ohlcv_load(testdatadir, caplog):
|
||||
assert df.columns.equals(df1.columns)
|
||||
|
||||
|
||||
def test_datahandler_ohlcv_data_min_max(testdatadir):
|
||||
dh = JsonDataHandler(testdatadir)
|
||||
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '5m', 'spot')
|
||||
assert len(min_max) == 2
|
||||
|
||||
# Empty pair
|
||||
min_max = dh.ohlcv_data_min_max('UNITTEST/BTC', '8m', 'spot')
|
||||
assert len(min_max) == 2
|
||||
assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc)
|
||||
assert min_max[0] == min_max[1]
|
||||
# Empty pair2
|
||||
min_max = dh.ohlcv_data_min_max('NOPAIR/XXX', '4m', 'spot')
|
||||
assert len(min_max) == 2
|
||||
assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc)
|
||||
assert min_max[0] == min_max[1]
|
||||
|
||||
|
||||
def test_datahandler__check_empty_df(testdatadir, caplog):
|
||||
dh = JsonDataHandler(testdatadir)
|
||||
expected_text = r"Price jump in UNITTEST/USDT, 1h, spot between"
|
||||
|
@@ -3,8 +3,11 @@ from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has_re, patch_exchange,
|
||||
patched_configuration_load_config_file)
|
||||
@@ -51,3 +54,32 @@ def test_freqai_backtest_load_data(freqai_conf, mocker, caplog):
|
||||
assert log_has_re('Increasing startup_candle_count for freqai to.*', caplog)
|
||||
|
||||
Backtesting.cleanup()
|
||||
|
||||
|
||||
def test_freqai_backtest_live_models_model_not_found(freqai_conf, mocker, testdatadir, caplog):
|
||||
patch_exchange(mocker)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['HULUMULU/USDT', 'XRP/USDT']))
|
||||
mocker.patch('freqtrade.optimize.backtesting.history.load_data')
|
||||
mocker.patch('freqtrade.optimize.backtesting.history.get_timerange', return_value=(now, now))
|
||||
freqai_conf["timerange"] = ""
|
||||
patched_configuration_load_config_file(mocker, freqai_conf)
|
||||
|
||||
args = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--datadir', str(testdatadir),
|
||||
'--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'),
|
||||
'--timeframe', '5m',
|
||||
'--freqai-backtest-live-models'
|
||||
]
|
||||
args = get_args(args)
|
||||
bt_config = setup_optimize_configuration(args, RunMode.BACKTEST)
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match=r".* Saved models are required to run backtest .*"):
|
||||
Backtesting(bt_config)
|
||||
|
||||
Backtesting.cleanup()
|
||||
|
@@ -22,6 +22,7 @@ def test_update_historic_data(mocker, freqai_conf):
|
||||
historic_candles = len(freqai.dd.historic_data["ADA/BTC"]["5m"])
|
||||
dp_candles = len(strategy.dp.get_pair_dataframe("ADA/BTC", "5m"))
|
||||
candle_difference = dp_candles - historic_candles
|
||||
freqai.dk.pair = "ADA/BTC"
|
||||
freqai.dd.update_historic_data(strategy, freqai.dk)
|
||||
|
||||
updated_historic_candles = len(freqai.dd.historic_data["ADA/BTC"]["5m"])
|
||||
|
@@ -1,13 +1,18 @@
|
||||
import shutil
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from tests.conftest import log_has_re
|
||||
from tests.freqai.conftest import (get_patched_data_kitchen, make_data_dictionary,
|
||||
make_unfiltered_dataframe)
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from freqtrade.freqai.utils import get_timerange_backtest_live_models
|
||||
from tests.conftest import get_patched_exchange, log_has_re
|
||||
from tests.freqai.conftest import (get_patched_data_kitchen, get_patched_freqai_strategy,
|
||||
make_data_dictionary, make_unfiltered_dataframe)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -159,3 +164,98 @@ def test_make_train_test_datasets(mocker, freqai_conf):
|
||||
assert data_dictionary
|
||||
assert len(data_dictionary) == 7
|
||||
assert len(data_dictionary['train_features'].index) == 1916
|
||||
|
||||
|
||||
def test_get_pairs_timestamp_validation(mocker, freqai_conf):
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.live = True
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
freqai_conf['freqai'].update({"identifier": "invalid_id"})
|
||||
model_path = freqai.dk.get_full_models_path(freqai_conf)
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
match=r'.*required to run backtest with the freqai-backtest-live-models.*'
|
||||
):
|
||||
freqai.dk.get_assets_timestamps_training_from_ready_models(model_path)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model', [
|
||||
'LightGBMRegressor'
|
||||
])
|
||||
def test_get_timerange_from_ready_models(mocker, freqai_conf, model):
|
||||
freqai_conf.update({"freqaimodel": model})
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.update({"strategy": "freqai_test_strat"})
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.live = True
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
timerange = TimeRange.parse_timerange("20180101-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
freqai.dd.pair_dict = MagicMock()
|
||||
|
||||
data_load_timerange = TimeRange.parse_timerange("20180101-20180130")
|
||||
|
||||
# 1516233600 (2018-01-18 00:00) - Start Training 1
|
||||
# 1516406400 (2018-01-20 00:00) - End Training 1 (Backtest slice 1)
|
||||
# 1516579200 (2018-01-22 00:00) - End Training 2 (Backtest slice 2)
|
||||
# 1516838400 (2018-01-25 00:00) - End Timerange
|
||||
|
||||
new_timerange = TimeRange("date", "date", 1516233600, 1516406400)
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
|
||||
new_timerange = TimeRange("date", "date", 1516406400, 1516579200)
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
|
||||
model_path = freqai.dk.get_full_models_path(freqai_conf)
|
||||
(backtesting_timerange,
|
||||
pairs_end_dates) = freqai.dk.get_timerange_and_assets_end_dates_from_ready_models(
|
||||
models_path=model_path)
|
||||
|
||||
assert len(pairs_end_dates["ADA"]) == 2
|
||||
assert backtesting_timerange.startts == 1516406400
|
||||
assert backtesting_timerange.stopts == 1516838400
|
||||
|
||||
backtesting_string_timerange = get_timerange_backtest_live_models(freqai_conf)
|
||||
assert backtesting_string_timerange == '20180120-20180125'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model', [
|
||||
'LightGBMRegressor'
|
||||
])
|
||||
def test_get_full_model_path(mocker, freqai_conf, model):
|
||||
freqai_conf.update({"freqaimodel": model})
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.update({"strategy": "freqai_test_strat"})
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.live = True
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
freqai.dd.pair_dict = MagicMock()
|
||||
|
||||
data_load_timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
new_timerange = TimeRange.parse_timerange("20180120-20180130")
|
||||
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
|
||||
model_path = freqai.dk.get_full_models_path(freqai_conf)
|
||||
assert model_path.is_dir() is True
|
||||
|
@@ -27,16 +27,16 @@ def is_mac() -> bool:
|
||||
return "Darwin" in machine
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model', [
|
||||
'LightGBMRegressor',
|
||||
'XGBoostRegressor',
|
||||
'XGBoostRFRegressor',
|
||||
'CatboostRegressor',
|
||||
'ReinforcementLearner',
|
||||
'ReinforcementLearner_multiproc',
|
||||
'ReinforcementLearner_test_4ac'
|
||||
@pytest.mark.parametrize('model, pca, dbscan', [
|
||||
('LightGBMRegressor', True, False),
|
||||
('XGBoostRegressor', False, True),
|
||||
('XGBoostRFRegressor', False, False),
|
||||
('CatboostRegressor', False, False),
|
||||
('ReinforcementLearner', False, False),
|
||||
('ReinforcementLearner_multiproc', False, False),
|
||||
('ReinforcementLearner_test_4ac', False, False)
|
||||
])
|
||||
def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model):
|
||||
def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan):
|
||||
if is_arm() and model == 'CatboostRegressor':
|
||||
pytest.skip("CatBoost is not supported on ARM")
|
||||
|
||||
@@ -47,6 +47,8 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model):
|
||||
freqai_conf.update({"freqaimodel": model})
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.update({"strategy": "freqai_test_strat"})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"principal_component_analysis": pca})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"use_DBSCAN_to_remove_outliers": dbscan})
|
||||
|
||||
if 'ReinforcementLearner' in model:
|
||||
model_save_ext = 'zip'
|
||||
@@ -89,17 +91,19 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model):
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model', [
|
||||
'LightGBMRegressorMultiTarget',
|
||||
'XGBoostRegressorMultiTarget',
|
||||
'CatboostRegressorMultiTarget',
|
||||
@pytest.mark.parametrize('model, strat', [
|
||||
('LightGBMRegressorMultiTarget', "freqai_test_multimodel_strat"),
|
||||
('XGBoostRegressorMultiTarget', "freqai_test_multimodel_strat"),
|
||||
('CatboostRegressorMultiTarget', "freqai_test_multimodel_strat"),
|
||||
('LightGBMClassifierMultiTarget', "freqai_test_multimodel_classifier_strat"),
|
||||
('CatboostClassifierMultiTarget', "freqai_test_multimodel_classifier_strat")
|
||||
])
|
||||
def test_extract_data_and_train_model_MultiTargets(mocker, freqai_conf, model):
|
||||
if is_arm() and model == 'CatboostRegressorMultiTarget':
|
||||
def test_extract_data_and_train_model_MultiTargets(mocker, freqai_conf, model, strat):
|
||||
if is_arm() and 'Catboost' in model:
|
||||
pytest.skip("CatBoost is not supported on ARM")
|
||||
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.update({"strategy": "freqai_test_multimodel_strat"})
|
||||
freqai_conf.update({"strategy": strat})
|
||||
freqai_conf.update({"freqaimodel": model})
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
@@ -216,6 +220,7 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
|
||||
corr_df, base_df = freqai.dd.get_base_and_corr_dataframes(sub_timerange, "LTC/BTC", freqai.dk)
|
||||
|
||||
df = freqai.dk.use_strategy_to_populate_indicators(strategy, corr_df, base_df, "LTC/BTC")
|
||||
df = freqai.cache_corr_pairlist_dfs(df, freqai.dk)
|
||||
for i in range(5):
|
||||
df[f'%-constant_{i}'] = i
|
||||
# df.loc[:, f'%-constant_{i}'] = i
|
||||
@@ -362,6 +367,7 @@ def test_follow_mode(mocker, freqai_conf):
|
||||
|
||||
df = strategy.dp.get_pair_dataframe('ADA/BTC', '5m')
|
||||
|
||||
freqai.dk.pair = "ADA/BTC"
|
||||
freqai.start_live(df, metadata, strategy, freqai.dk)
|
||||
|
||||
assert len(freqai.dk.return_dataframe.index) == 5702
|
||||
|
@@ -764,6 +764,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
'max_rate': [0.10501, 0.1038888],
|
||||
'is_open': [False, False],
|
||||
'enter_tag': [None, None],
|
||||
"leverage": [1.0, 1.0],
|
||||
"is_short": [False, False],
|
||||
'open_timestamp': [1517251200000, 1517283000000],
|
||||
'close_timestamp': [1517265300000, 1517285400000],
|
||||
@@ -788,13 +789,14 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||
assert len(t['orders']) == 2
|
||||
ln = data_pair.loc[data_pair["date"] == t["open_date"]]
|
||||
# Check open trade rate alignes to open rate
|
||||
assert ln is not None
|
||||
assert not ln.empty
|
||||
assert round(ln.iloc[0]["open"], 6) == round(t["open_rate"], 6)
|
||||
# check close trade rate alignes to close rate or is between high and low
|
||||
ln = data_pair.loc[data_pair["date"] == t["close_date"]]
|
||||
assert (round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or
|
||||
round(ln.iloc[0]["low"], 6) < round(
|
||||
t["close_rate"], 6) < round(ln.iloc[0]["high"], 6))
|
||||
ln1 = data_pair.loc[data_pair["date"] == t["close_date"]]
|
||||
assert not ln1.empty
|
||||
assert (round(ln1.iloc[0]["open"], 6) == round(t["close_rate"], 6) or
|
||||
round(ln1.iloc[0]["low"], 6) < round(
|
||||
t["close_rate"], 6) < round(ln1.iloc[0]["high"], 6))
|
||||
|
||||
|
||||
def test_backtest_timedout_entry_orders(default_conf, fee, mocker, testdatadir) -> None:
|
||||
|
@@ -72,6 +72,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
|
||||
'max_rate': [0.10481985, 0.1038888],
|
||||
'is_open': [False, False],
|
||||
'enter_tag': [None, None],
|
||||
'leverage': [1.0, 1.0],
|
||||
'is_short': [False, False],
|
||||
'open_timestamp': [1517251200000, 1517283000000],
|
||||
'close_timestamp': [1517265300000, 1517285400000],
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
import logging
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pandas as pd
|
||||
@@ -719,15 +721,26 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
|
||||
def test_ShuffleFilter_init(mocker, whitelist_conf, caplog) -> None:
|
||||
whitelist_conf['pairlists'] = [
|
||||
{"method": "StaticPairList"},
|
||||
{"method": "ShuffleFilter", "seed": 42}
|
||||
{"method": "ShuffleFilter", "seed": 43}
|
||||
]
|
||||
|
||||
exchange = get_patched_exchange(mocker, whitelist_conf)
|
||||
PairListManager(exchange, whitelist_conf)
|
||||
assert log_has("Backtesting mode detected, applying seed value: 42", caplog)
|
||||
plm = PairListManager(exchange, whitelist_conf)
|
||||
assert log_has("Backtesting mode detected, applying seed value: 43", caplog)
|
||||
|
||||
with time_machine.travel("2021-09-01 05:01:00 +00:00") as t:
|
||||
plm.refresh_pairlist()
|
||||
pl1 = deepcopy(plm.whitelist)
|
||||
plm.refresh_pairlist()
|
||||
assert plm.whitelist == pl1
|
||||
|
||||
t.shift(timedelta(minutes=10))
|
||||
plm.refresh_pairlist()
|
||||
assert plm.whitelist != pl1
|
||||
|
||||
caplog.clear()
|
||||
whitelist_conf['runmode'] = RunMode.DRY_RUN
|
||||
PairListManager(exchange, whitelist_conf)
|
||||
plm = PairListManager(exchange, whitelist_conf)
|
||||
assert not log_has("Backtesting mode detected, applying seed value: 42", caplog)
|
||||
assert log_has("Live mode detected, not applying seed.", caplog)
|
||||
|
||||
|
@@ -1461,6 +1461,7 @@ def test_api_strategies(botclient, tmpdir):
|
||||
'StrategyTestV3Futures',
|
||||
'freqai_rl_test_strat',
|
||||
'freqai_test_classifier',
|
||||
'freqai_test_multimodel_classifier_strat',
|
||||
'freqai_test_multimodel_strat',
|
||||
'freqai_test_strat'
|
||||
]}
|
||||
|
138
tests/strategy/strats/freqai_test_multimodel_classifier_strat.py
Normal file
138
tests/strategy/strats/freqai_test_multimodel_classifier_strat.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.strategy import DecimalParameter, IntParameter, IStrategy, merge_informative_pair
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class freqai_test_multimodel_classifier_strat(IStrategy):
|
||||
"""
|
||||
Test strategy - used for testing freqAI multimodel functionalities.
|
||||
DO not use in production.
|
||||
"""
|
||||
|
||||
minimal_roi = {"0": 0.1, "240": -1}
|
||||
|
||||
plot_config = {
|
||||
"main_plot": {},
|
||||
"subplots": {
|
||||
"prediction": {"prediction": {"color": "blue"}},
|
||||
"target_roi": {
|
||||
"target_roi": {"color": "brown"},
|
||||
},
|
||||
"do_predict": {
|
||||
"do_predict": {"color": "brown"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
process_only_new_candles = True
|
||||
stoploss = -0.05
|
||||
use_exit_signal = True
|
||||
startup_candle_count: int = 300
|
||||
can_short = False
|
||||
|
||||
linear_roi_offset = DecimalParameter(
|
||||
0.00, 0.02, default=0.005, space="sell", optimize=False, load=True
|
||||
)
|
||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
||||
|
||||
def populate_any_indicators(
|
||||
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
||||
):
|
||||
|
||||
coin = pair.split('/')[0]
|
||||
|
||||
if informative is None:
|
||||
informative = self.dp.get_pair_dataframe(pair, tf)
|
||||
|
||||
# first loop is automatically duplicating indicators for time periods
|
||||
for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]:
|
||||
|
||||
t = int(t)
|
||||
informative[f"%-{coin}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t)
|
||||
informative[f"%-{coin}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t)
|
||||
informative[f"%-{coin}adx-period_{t}"] = ta.ADX(informative, window=t)
|
||||
|
||||
informative[f"%-{coin}pct-change"] = informative["close"].pct_change()
|
||||
informative[f"%-{coin}raw_volume"] = informative["volume"]
|
||||
informative[f"%-{coin}raw_price"] = informative["close"]
|
||||
|
||||
indicators = [col for col in informative if col.startswith("%")]
|
||||
# This loop duplicates and shifts all indicators to add a sense of recency to data
|
||||
for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1):
|
||||
if n == 0:
|
||||
continue
|
||||
informative_shift = informative[indicators].shift(n)
|
||||
informative_shift = informative_shift.add_suffix("_shift-" + str(n))
|
||||
informative = pd.concat((informative, informative_shift), axis=1)
|
||||
|
||||
df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True)
|
||||
skip_columns = [
|
||||
(s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"]
|
||||
]
|
||||
df = df.drop(columns=skip_columns)
|
||||
|
||||
# Add generalized indicators here (because in live, it will call this
|
||||
# function to populate indicators during training). Notice how we ensure not to
|
||||
# add them multiple times
|
||||
if set_generalized_indicators:
|
||||
df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7
|
||||
df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25
|
||||
|
||||
# user adds targets here by prepending them with &- (see convention below)
|
||||
# If user wishes to use multiple targets, a multioutput prediction model
|
||||
# needs to be used such as templates/CatboostPredictionMultiModel.py
|
||||
df['&s-up_or_down'] = np.where(df["close"].shift(-50) >
|
||||
df["close"], 'up', 'down')
|
||||
|
||||
df['&s-up_or_down2'] = np.where(df["close"].shift(-50) >
|
||||
df["close"], 'up2', 'down2')
|
||||
|
||||
return df
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
|
||||
self.freqai_info = self.config["freqai"]
|
||||
|
||||
dataframe = self.freqai.start(dataframe, metadata, self)
|
||||
|
||||
dataframe["target_roi"] = dataframe["&-s_close_mean"] + dataframe["&-s_close_std"] * 1.25
|
||||
dataframe["sell_roi"] = dataframe["&-s_close_mean"] - dataframe["&-s_close_std"] * 1.25
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
|
||||
enter_long_conditions = [df["do_predict"] == 1, df["&-s_close"] > df["target_roi"]]
|
||||
|
||||
if enter_long_conditions:
|
||||
df.loc[
|
||||
reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]
|
||||
] = (1, "long")
|
||||
|
||||
enter_short_conditions = [df["do_predict"] == 1, df["&-s_close"] < df["sell_roi"]]
|
||||
|
||||
if enter_short_conditions:
|
||||
df.loc[
|
||||
reduce(lambda x, y: x & y, enter_short_conditions), ["enter_short", "enter_tag"]
|
||||
] = (1, "short")
|
||||
|
||||
return df
|
||||
|
||||
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
exit_long_conditions = [df["do_predict"] == 1, df["&-s_close"] < df["sell_roi"] * 0.25]
|
||||
if exit_long_conditions:
|
||||
df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1
|
||||
|
||||
exit_short_conditions = [df["do_predict"] == 1, df["&-s_close"] > df["target_roi"] * 0.25]
|
||||
if exit_short_conditions:
|
||||
df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1
|
||||
|
||||
return df
|
@@ -1538,3 +1538,85 @@ def test_flat_vars_to_nested_dict(caplog):
|
||||
|
||||
assert log_has("Loading variable 'FREQTRADE__EXCHANGE__SOME_SETTING'", caplog)
|
||||
assert not log_has("Loading variable 'NOT_RELEVANT'", caplog)
|
||||
|
||||
|
||||
def test_setup_hyperopt_freqai(mocker, default_conf, caplog) -> None:
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
mocker.patch(
|
||||
'freqtrade.configuration.configuration.create_datadir',
|
||||
lambda c, x: x
|
||||
)
|
||||
mocker.patch(
|
||||
'freqtrade.configuration.configuration.create_userdata_dir',
|
||||
lambda x, *args, **kwargs: Path(x)
|
||||
)
|
||||
arglist = [
|
||||
'hyperopt',
|
||||
'--config', 'config.json',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--timerange', '20220801-20220805',
|
||||
"--freqaimodel",
|
||||
"LightGBMRegressorMultiTarget",
|
||||
"--analyze-per-epoch"
|
||||
]
|
||||
|
||||
args = Arguments(arglist).get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
config = configuration.get_config()
|
||||
config['freqai'] = {
|
||||
"enabled": True
|
||||
}
|
||||
with pytest.raises(
|
||||
OperationalException, match=r".*analyze-per-epoch parameter is not supported.*"
|
||||
):
|
||||
validate_config_consistency(config)
|
||||
|
||||
|
||||
def test_setup_freqai_backtesting(mocker, default_conf, caplog) -> None:
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
mocker.patch(
|
||||
'freqtrade.configuration.configuration.create_datadir',
|
||||
lambda c, x: x
|
||||
)
|
||||
mocker.patch(
|
||||
'freqtrade.configuration.configuration.create_userdata_dir',
|
||||
lambda x, *args, **kwargs: Path(x)
|
||||
)
|
||||
arglist = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--timerange', '20220801-20220805',
|
||||
"--freqaimodel",
|
||||
"LightGBMRegressorMultiTarget",
|
||||
"--freqai-backtest-live-models"
|
||||
]
|
||||
|
||||
args = Arguments(arglist).get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
config = configuration.get_config()
|
||||
config['runmode'] = RunMode.BACKTEST
|
||||
|
||||
with pytest.raises(
|
||||
OperationalException, match=r".*--freqai-backtest-live-models parameter is only.*"
|
||||
):
|
||||
validate_config_consistency(config)
|
||||
|
||||
conf = deepcopy(config)
|
||||
conf['freqai'] = {
|
||||
"enabled": True
|
||||
}
|
||||
with pytest.raises(
|
||||
OperationalException, match=r".* timerange parameter is not supported with .*"
|
||||
):
|
||||
validate_config_consistency(conf)
|
||||
|
||||
conf['timerange'] = None
|
||||
conf['freqai_backtest_live_models'] = False
|
||||
|
||||
with pytest.raises(
|
||||
OperationalException, match=r".* pass --timerange if you intend to use FreqAI .*"
|
||||
):
|
||||
validate_config_consistency(conf)
|
||||
|
@@ -5305,7 +5305,7 @@ def test_get_valid_price(mocker, default_conf_usdt) -> None:
|
||||
])
|
||||
def test_update_funding_fees_schedule(mocker, default_conf, trading_mode, calls, time_machine,
|
||||
t1, t2):
|
||||
time_machine.move_to(f"{t1} +00:00")
|
||||
time_machine.move_to(f"{t1} +00:00", tick=False)
|
||||
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
@@ -5314,7 +5314,7 @@ def test_update_funding_fees_schedule(mocker, default_conf, trading_mode, calls,
|
||||
default_conf['margin_mode'] = 'isolated'
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
time_machine.move_to(f"{t2} +00:00")
|
||||
time_machine.move_to(f"{t2} +00:00", tick=False)
|
||||
# Check schedule jobs in debugging with freqtrade._schedule.jobs
|
||||
freqtrade._schedule.run_pending()
|
||||
|
||||
|
@@ -113,6 +113,16 @@ def test_throttle_sleep_time(mocker, default_conf, caplog) -> None:
|
||||
# 300 (5m) - 60 (1m - see set time above) - 5 (duration of throttled_func) = 235
|
||||
assert 235.2 < sleep_mock.call_args[0][0] < 235.6
|
||||
|
||||
t.move_to("2022-09-01 05:04:51 +00:00")
|
||||
sleep_mock.reset_mock()
|
||||
# Offset of 5s, so we hit the sweet-spot between "candle" and "candle offset"
|
||||
# Which should not get a throttle iteration to avoid late candle fetching
|
||||
assert worker._throttle(throttled_func, throttle_secs=10, timeframe='5m',
|
||||
timeframe_offset=5, x=1.2) == 42
|
||||
assert sleep_mock.call_count == 1
|
||||
# Time is slightly bigger than throttle secs due to the high timeframe offset.
|
||||
assert 11.1 < sleep_mock.call_args[0][0] < 13.2
|
||||
|
||||
|
||||
def test_throttle_with_assets(mocker, default_conf) -> None:
|
||||
def throttled_func(nb_assets=-1):
|
||||
|
Reference in New Issue
Block a user