mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-04-29 21:44:06 +00:00
Merge branch 'develop' into pr/Axel-CH/8779
This commit is contained in:
@@ -641,7 +641,7 @@ def test_get_ui_download_url_direct(mocker):
|
||||
|
||||
|
||||
def test_download_data_keyboardInterrupt(mocker, markets):
|
||||
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
|
||||
dl_mock = mocker.patch('freqtrade.commands.data_commands.download_data_main',
|
||||
MagicMock(side_effect=KeyboardInterrupt))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
|
||||
@@ -660,7 +660,7 @@ def test_download_data_keyboardInterrupt(mocker, markets):
|
||||
|
||||
|
||||
def test_download_data_timerange(mocker, markets):
|
||||
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
|
||||
@@ -708,10 +708,10 @@ def test_download_data_timerange(mocker, markets):
|
||||
|
||||
|
||||
def test_download_data_no_markets(mocker, caplog):
|
||||
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker, id='binance')
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
args = [
|
||||
"download-data",
|
||||
"--exchange", "binance",
|
||||
@@ -723,11 +723,11 @@ def test_download_data_no_markets(mocker, caplog):
|
||||
assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange Binance.", caplog)
|
||||
|
||||
|
||||
def test_download_data_no_exchange(mocker, caplog):
|
||||
mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
|
||||
def test_download_data_no_exchange(mocker):
|
||||
mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
args = [
|
||||
"download-data",
|
||||
]
|
||||
@@ -740,7 +740,7 @@ def test_download_data_no_exchange(mocker, caplog):
|
||||
|
||||
def test_download_data_no_pairs(mocker):
|
||||
|
||||
mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
|
||||
mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
|
||||
@@ -758,7 +758,7 @@ def test_download_data_no_pairs(mocker):
|
||||
|
||||
def test_download_data_all_pairs(mocker, markets):
|
||||
|
||||
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
|
||||
@@ -792,13 +792,13 @@ def test_download_data_all_pairs(mocker, markets):
|
||||
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
|
||||
|
||||
|
||||
def test_download_data_trades(mocker, caplog):
|
||||
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_trades_data',
|
||||
def test_download_data_trades(mocker):
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_trades_data',
|
||||
MagicMock(return_value=[]))
|
||||
convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv',
|
||||
convert_mock = mocker.patch('freqtrade.data.history.history_utils.convert_trades_to_ohlcv',
|
||||
MagicMock(return_value=[]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
args = [
|
||||
"download-data",
|
||||
"--exchange", "kraken",
|
||||
@@ -829,7 +829,7 @@ def test_download_data_trades(mocker, caplog):
|
||||
|
||||
def test_download_data_data_invalid(mocker):
|
||||
patch_exchange(mocker, id="kraken")
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value={}))
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
args = [
|
||||
"download-data",
|
||||
"--exchange", "kraken",
|
||||
|
||||
96
tests/data/test_download_data.py
Normal file
96
tests/data/test_download_data.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.configuration.config_setup import setup_utils_configuration
|
||||
from freqtrade.data.history.history_utils import download_data_main
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from tests.conftest import EXMS, log_has, patch_exchange
|
||||
|
||||
|
||||
def test_download_data_main_no_markets(mocker, caplog):
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker, id='binance')
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE)
|
||||
config.update({
|
||||
"days": 20,
|
||||
"pairs": ["ETH/BTC", "XRP/BTC"],
|
||||
"timeframes": ["5m", "1h"]
|
||||
})
|
||||
download_data_main(config)
|
||||
assert dl_mock.call_args[1]['timerange'].starttype == "date"
|
||||
assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange Binance.", caplog)
|
||||
|
||||
|
||||
def test_download_data_main_all_pairs(mocker, markets):
|
||||
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
|
||||
|
||||
config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE)
|
||||
config.update({
|
||||
"pairs": [".*/USDT"],
|
||||
"timeframes": ["5m", "1h"]
|
||||
})
|
||||
download_data_main(config)
|
||||
expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
|
||||
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
|
||||
assert dl_mock.call_count == 1
|
||||
|
||||
dl_mock.reset_mock()
|
||||
|
||||
config.update({
|
||||
"pairs": [".*/USDT"],
|
||||
"timeframes": ["5m", "1h"],
|
||||
"include_inactive": True
|
||||
})
|
||||
download_data_main(config)
|
||||
expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
|
||||
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
|
||||
|
||||
|
||||
def test_download_data_main_trades(mocker):
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils.refresh_backtest_trades_data',
|
||||
MagicMock(return_value=[]))
|
||||
convert_mock = mocker.patch('freqtrade.data.history.history_utils.convert_trades_to_ohlcv',
|
||||
MagicMock(return_value=[]))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
config = setup_utils_configuration({"exchange": "binance"}, RunMode.UTIL_EXCHANGE)
|
||||
config.update({
|
||||
"days": 20,
|
||||
"pairs": ["ETH/BTC", "XRP/BTC"],
|
||||
"timeframes": ["5m", "1h"],
|
||||
"download_trades": True,
|
||||
})
|
||||
download_data_main(config)
|
||||
|
||||
assert dl_mock.call_args[1]['timerange'].starttype == "date"
|
||||
assert dl_mock.call_count == 1
|
||||
assert convert_mock.call_count == 1
|
||||
config.update({
|
||||
"download_trades": True,
|
||||
"trading_mode": "futures",
|
||||
})
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match="Trade download not supported for futures."):
|
||||
download_data_main(config)
|
||||
|
||||
|
||||
def test_download_data_main_data_invalid(mocker):
|
||||
patch_exchange(mocker, id="kraken")
|
||||
mocker.patch(f'{EXMS}.get_markets', return_value={})
|
||||
config = setup_utils_configuration({"exchange": "kraken"}, RunMode.UTIL_EXCHANGE)
|
||||
config.update({
|
||||
"days": 20,
|
||||
"pairs": ["ETH/BTC", "XRP/BTC"],
|
||||
"timeframes": ["5m", "1h"],
|
||||
})
|
||||
with pytest.raises(OperationalException, match=r"Historic klines not available for .*"):
|
||||
download_data_main(config)
|
||||
@@ -1,6 +1,7 @@
|
||||
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
||||
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
@@ -503,9 +504,10 @@ def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> No
|
||||
])
|
||||
def test_refresh_backtest_ohlcv_data(
|
||||
mocker, default_conf, markets, caplog, testdatadir, trademode, callcount):
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils._download_pair_history',
|
||||
MagicMock())
|
||||
caplog.set_level(logging.DEBUG)
|
||||
dl_mock = mocker.patch('freqtrade.data.history.history_utils._download_pair_history')
|
||||
mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets))
|
||||
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
mocker.patch.object(Path, "unlink", MagicMock())
|
||||
|
||||
@@ -520,7 +522,7 @@ def test_refresh_backtest_ohlcv_data(
|
||||
assert dl_mock.call_count == callcount
|
||||
assert dl_mock.call_args[1]['timerange'].starttype == 'date'
|
||||
|
||||
assert log_has("Downloading pair ETH/BTC, interval 1m.", caplog)
|
||||
assert log_has_re(r"Downloading pair ETH/BTC, .* interval 1m\.", caplog)
|
||||
|
||||
|
||||
def test_download_data_no_markets(mocker, default_conf, caplog, testdatadir):
|
||||
|
||||
@@ -9,9 +9,9 @@ from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from tests.conftest import get_patched_exchange, log_has_re
|
||||
from tests.conftest import get_patched_exchange
|
||||
from tests.freqai.conftest import (get_patched_data_kitchen, get_patched_freqai_strategy,
|
||||
make_data_dictionary, make_unfiltered_dataframe)
|
||||
make_unfiltered_dataframe)
|
||||
from tests.freqai.test_freqai_interface import is_mac
|
||||
|
||||
|
||||
@@ -72,68 +72,6 @@ def test_check_if_model_expired(mocker, freqai_conf):
|
||||
shutil.rmtree(Path(dk.full_path))
|
||||
|
||||
|
||||
def test_use_DBSCAN_to_remove_outliers(mocker, freqai_conf, caplog):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
# freqai_conf['freqai']['feature_parameters'].update({"outlier_protection_percentage": 1})
|
||||
freqai.dk.use_DBSCAN_to_remove_outliers(predict=False)
|
||||
assert log_has_re(r"DBSCAN found eps of 1\.7\d\.", caplog)
|
||||
|
||||
|
||||
def test_compute_distances(mocker, freqai_conf):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai_conf['freqai']['feature_parameters'].update({"DI_threshold": 1})
|
||||
avg_mean_dist = freqai.dk.compute_distances()
|
||||
assert round(avg_mean_dist, 2) == 1.98
|
||||
|
||||
|
||||
def test_use_SVM_to_remove_outliers_and_outlier_protection(mocker, freqai_conf, caplog):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai_conf['freqai']['feature_parameters'].update({"outlier_protection_percentage": 0.1})
|
||||
freqai.dk.use_SVM_to_remove_outliers(predict=False)
|
||||
assert log_has_re(
|
||||
"SVM detected 7.83%",
|
||||
caplog,
|
||||
)
|
||||
|
||||
|
||||
def test_compute_inlier_metric(mocker, freqai_conf, caplog):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai_conf['freqai']['feature_parameters'].update({"inlier_metric_window": 10})
|
||||
freqai.dk.compute_inlier_metric(set_='train')
|
||||
assert log_has_re(
|
||||
"Inlier metric computed and added to features.",
|
||||
caplog,
|
||||
)
|
||||
|
||||
|
||||
def test_add_noise_to_training_features(mocker, freqai_conf):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai_conf['freqai']['feature_parameters'].update({"noise_standard_deviation": 0.1})
|
||||
freqai.dk.add_noise_to_training_features()
|
||||
|
||||
|
||||
def test_remove_beginning_points_from_data_dict(mocker, freqai_conf):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai.dk.remove_beginning_points_from_data_dict(set_='train')
|
||||
|
||||
|
||||
def test_principal_component_analysis(mocker, freqai_conf, caplog):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai.dk.principal_component_analysis()
|
||||
assert log_has_re(
|
||||
"reduced feature dimension by",
|
||||
caplog,
|
||||
)
|
||||
|
||||
|
||||
def test_normalize_data(mocker, freqai_conf):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
data_dict = freqai.dk.data_dictionary
|
||||
freqai.dk.normalize_data(data_dict)
|
||||
assert any('_max' in entry for entry in freqai.dk.data.keys())
|
||||
assert any('_min' in entry for entry in freqai.dk.data.keys())
|
||||
|
||||
|
||||
def test_filter_features(mocker, freqai_conf):
|
||||
freqai, unfiltered_dataframe = make_unfiltered_dataframe(mocker, freqai_conf)
|
||||
freqai.dk.find_features(unfiltered_dataframe)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
@@ -37,21 +38,22 @@ def can_run_model(model: str) -> None:
|
||||
pytest.skip("Reinforcement learning / PyTorch module not available on intel based Mac OS.")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model, pca, dbscan, float32, can_short, shuffle, buffer', [
|
||||
('LightGBMRegressor', True, False, True, True, False, 0),
|
||||
('XGBoostRegressor', False, True, False, True, False, 10),
|
||||
('XGBoostRFRegressor', False, False, False, True, False, 0),
|
||||
('CatboostRegressor', False, False, False, True, True, 0),
|
||||
('PyTorchMLPRegressor', False, False, False, False, False, 0),
|
||||
('PyTorchTransformerRegressor', False, False, False, False, False, 0),
|
||||
('ReinforcementLearner', False, True, False, True, False, 0),
|
||||
('ReinforcementLearner_multiproc', False, False, False, True, False, 0),
|
||||
('ReinforcementLearner_test_3ac', False, False, False, False, False, 0),
|
||||
('ReinforcementLearner_test_3ac', False, False, False, True, False, 0),
|
||||
('ReinforcementLearner_test_4ac', False, False, False, True, False, 0),
|
||||
@pytest.mark.parametrize('model, pca, dbscan, float32, can_short, shuffle, buffer, noise', [
|
||||
('LightGBMRegressor', True, False, True, True, False, 0, 0),
|
||||
('XGBoostRegressor', False, True, False, True, False, 10, 0.05),
|
||||
('XGBoostRFRegressor', False, False, False, True, False, 0, 0),
|
||||
('CatboostRegressor', False, False, False, True, True, 0, 0),
|
||||
('PyTorchMLPRegressor', False, False, False, False, False, 0, 0),
|
||||
('PyTorchTransformerRegressor', False, False, False, False, False, 0, 0),
|
||||
('ReinforcementLearner', False, True, False, True, False, 0, 0),
|
||||
('ReinforcementLearner_multiproc', False, False, False, True, False, 0, 0),
|
||||
('ReinforcementLearner_test_3ac', False, False, False, False, False, 0, 0),
|
||||
('ReinforcementLearner_test_3ac', False, False, False, True, False, 0, 0),
|
||||
('ReinforcementLearner_test_4ac', False, False, False, True, False, 0, 0),
|
||||
])
|
||||
def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
dbscan, float32, can_short, shuffle, buffer):
|
||||
dbscan, float32, can_short, shuffle,
|
||||
buffer, noise):
|
||||
|
||||
can_run_model(model)
|
||||
|
||||
@@ -68,12 +70,14 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
freqai_conf.update({"reduce_df_footprint": float32})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"shuffle_after_split": shuffle})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"buffer_train_data_candles": buffer})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"noise_standard_deviation": noise})
|
||||
|
||||
if 'ReinforcementLearner' in model:
|
||||
model_save_ext = 'zip'
|
||||
freqai_conf = make_rl_config(freqai_conf)
|
||||
# test the RL guardrails
|
||||
freqai_conf['freqai']['feature_parameters'].update({"use_SVM_to_remove_outliers": True})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"DI_threshold": 2})
|
||||
freqai_conf['freqai']['data_split_parameters'].update({'shuffle': True})
|
||||
|
||||
if 'test_3ac' in model or 'test_4ac' in model:
|
||||
@@ -162,7 +166,6 @@ def test_extract_data_and_train_model_MultiTargets(mocker, freqai_conf, model, s
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_model.joblib").is_file()
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_metadata.json").is_file()
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_trained_df.pkl").is_file()
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_svm_model.joblib").is_file()
|
||||
assert len(freqai.dk.data['training_features_list']) == 14
|
||||
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
@@ -218,7 +221,6 @@ def test_extract_data_and_train_model_Classifiers(mocker, freqai_conf, model):
|
||||
f"{freqai.dk.model_filename}_model{model_file_extension}").exists()
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_metadata.json").exists()
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_trained_df.pkl").exists()
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_svm_model.joblib").exists()
|
||||
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
|
||||
@@ -283,9 +285,6 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
|
||||
_, base_df = freqai.dd.get_base_and_corr_dataframes(sub_timerange, "LTC/BTC", freqai.dk)
|
||||
df = base_df[freqai_conf["timeframe"]]
|
||||
|
||||
for i in range(5):
|
||||
df[f'%-constant_{i}'] = i
|
||||
|
||||
metadata = {"pair": "LTC/BTC"}
|
||||
freqai.dk.set_paths('LTC/BTC', None)
|
||||
freqai.start_backtesting(df, metadata, freqai.dk, strategy)
|
||||
@@ -293,14 +292,6 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
|
||||
|
||||
assert len(model_folders) == num_files
|
||||
Trade.use_db = True
|
||||
assert log_has_re(
|
||||
"Removed features ",
|
||||
caplog,
|
||||
)
|
||||
assert log_has_re(
|
||||
"Removed 5 features from prediction features, ",
|
||||
caplog,
|
||||
)
|
||||
Backtesting.cleanup()
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
|
||||
@@ -425,36 +416,6 @@ def test_backtesting_fit_live_predictions(mocker, freqai_conf, caplog):
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
|
||||
|
||||
def test_principal_component_analysis(mocker, freqai_conf):
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.get("freqai", {}).get("feature_parameters", {}).update(
|
||||
{"princpial_component_analysis": "true"})
|
||||
|
||||
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)
|
||||
freqai.dk.live = True
|
||||
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.dk.set_paths('ADA/BTC', None)
|
||||
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
|
||||
assert Path(freqai.dk.data_path / f"{freqai.dk.model_filename}_pca_object.pkl")
|
||||
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
|
||||
|
||||
def test_plot_feature_importance(mocker, freqai_conf):
|
||||
|
||||
from freqtrade.freqai.utils import plot_feature_importance
|
||||
@@ -540,6 +501,7 @@ def test_get_required_data_timerange(mocker, freqai_conf):
|
||||
|
||||
|
||||
def test_download_all_data_for_training(mocker, freqai_conf, caplog, tmpdir):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
pairlist = PairListManager(exchange, freqai_conf)
|
||||
|
||||
366
tests/optimize/test_lookahead_analysis.py
Normal file
366
tests/optimize/test_lookahead_analysis.py
Normal file
@@ -0,0 +1,366 @@
|
||||
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.commands.optimize_commands import start_lookahead_analysis
|
||||
from freqtrade.data.history import get_timerange
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.lookahead_analysis import Analysis, LookaheadAnalysis
|
||||
from freqtrade.optimize.lookahead_analysis_helpers import LookaheadAnalysisSubFunctions
|
||||
from tests.conftest import EXMS, get_args, log_has_re, patch_exchange
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lookahead_conf(default_conf_usdt):
|
||||
default_conf_usdt['minimum_trade_amount'] = 10
|
||||
default_conf_usdt['targeted_trade_amount'] = 20
|
||||
default_conf_usdt['strategy_path'] = str(
|
||||
Path(__file__).parent.parent / "strategy/strats/lookahead_bias")
|
||||
default_conf_usdt['strategy'] = 'strategy_test_v3_with_lookahead_bias'
|
||||
default_conf_usdt['max_open_trades'] = 1
|
||||
default_conf_usdt['dry_run_wallet'] = 1000000000
|
||||
default_conf_usdt['pairs'] = ['UNITTEST/USDT']
|
||||
return default_conf_usdt
|
||||
|
||||
|
||||
def test_start_lookahead_analysis(mocker):
|
||||
single_mock = MagicMock()
|
||||
text_table_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.optimize.lookahead_analysis_helpers.LookaheadAnalysisSubFunctions',
|
||||
initialize_single_lookahead_analysis=single_mock,
|
||||
text_table_lookahead_analysis_instances=text_table_mock,
|
||||
)
|
||||
args = [
|
||||
"lookahead-analysis",
|
||||
"--strategy",
|
||||
"strategy_test_v3_with_lookahead_bias",
|
||||
"--strategy-path",
|
||||
str(Path(__file__).parent.parent / "strategy/strats/lookahead_bias"),
|
||||
"--pairs",
|
||||
"UNITTEST/BTC",
|
||||
"--max-open-trades",
|
||||
"1"
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
|
||||
start_lookahead_analysis(pargs)
|
||||
assert single_mock.call_count == 1
|
||||
assert text_table_mock.call_count == 1
|
||||
|
||||
single_mock.reset_mock()
|
||||
|
||||
# Test invalid config
|
||||
args = [
|
||||
"lookahead-analysis",
|
||||
"--strategy",
|
||||
"strategy_test_v3_with_lookahead_bias",
|
||||
"--strategy-path",
|
||||
str(Path(__file__).parent.parent / "strategy/strats/lookahead_bias"),
|
||||
"--targeted-trade-amount",
|
||||
"10",
|
||||
"--minimum-trade-amount",
|
||||
"20",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Targeted trade amount can't be smaller than minimum trade amount.*"):
|
||||
start_lookahead_analysis(pargs)
|
||||
|
||||
|
||||
def test_lookahead_helper_invalid_config(lookahead_conf) -> None:
|
||||
conf = deepcopy(lookahead_conf)
|
||||
conf['targeted_trade_amount'] = 10
|
||||
conf['minimum_trade_amount'] = 40
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Targeted trade amount can't be smaller than minimum trade amount.*"):
|
||||
LookaheadAnalysisSubFunctions.start(conf)
|
||||
|
||||
|
||||
def test_lookahead_helper_no_strategy_defined(lookahead_conf):
|
||||
conf = deepcopy(lookahead_conf)
|
||||
conf['pairs'] = ['UNITTEST/USDT']
|
||||
del conf['strategy']
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"No Strategy specified"):
|
||||
LookaheadAnalysisSubFunctions.start(conf)
|
||||
|
||||
|
||||
def test_lookahead_helper_start(lookahead_conf, mocker) -> None:
|
||||
single_mock = MagicMock()
|
||||
text_table_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.optimize.lookahead_analysis_helpers.LookaheadAnalysisSubFunctions',
|
||||
initialize_single_lookahead_analysis=single_mock,
|
||||
text_table_lookahead_analysis_instances=text_table_mock,
|
||||
)
|
||||
LookaheadAnalysisSubFunctions.start(lookahead_conf)
|
||||
assert single_mock.call_count == 1
|
||||
assert text_table_mock.call_count == 1
|
||||
|
||||
single_mock.reset_mock()
|
||||
text_table_mock.reset_mock()
|
||||
|
||||
|
||||
def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf):
|
||||
analysis = Analysis()
|
||||
analysis.has_bias = True
|
||||
analysis.total_signals = 5
|
||||
analysis.false_entry_signals = 4
|
||||
analysis.false_exit_signals = 3
|
||||
|
||||
strategy_obj = {
|
||||
'name': "strategy_test_v3_with_lookahead_bias",
|
||||
'location': Path(lookahead_conf['strategy_path'], f"{lookahead_conf['strategy']}.py")
|
||||
}
|
||||
|
||||
instance = LookaheadAnalysis(lookahead_conf, strategy_obj)
|
||||
instance.current_analysis = analysis
|
||||
table, headers, data = (LookaheadAnalysisSubFunctions.
|
||||
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
|
||||
|
||||
# check row contents for a try that has too few signals
|
||||
assert data[0][0] == 'strategy_test_v3_with_lookahead_bias.py'
|
||||
assert data[0][1] == 'strategy_test_v3_with_lookahead_bias'
|
||||
assert data[0][2].__contains__('too few trades')
|
||||
assert len(data[0]) == 3
|
||||
|
||||
# now check for an error which occured after enough trades
|
||||
analysis.total_signals = 12
|
||||
analysis.false_entry_signals = 11
|
||||
analysis.false_exit_signals = 10
|
||||
instance = LookaheadAnalysis(lookahead_conf, strategy_obj)
|
||||
instance.current_analysis = analysis
|
||||
table, headers, data = (LookaheadAnalysisSubFunctions.
|
||||
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
|
||||
assert data[0][2].__contains__("error")
|
||||
|
||||
# edit it into not showing an error
|
||||
instance.failed_bias_check = False
|
||||
table, headers, data = (LookaheadAnalysisSubFunctions.
|
||||
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
|
||||
assert data[0][0] == 'strategy_test_v3_with_lookahead_bias.py'
|
||||
assert data[0][1] == 'strategy_test_v3_with_lookahead_bias'
|
||||
assert data[0][2] # True
|
||||
assert data[0][3] == 12
|
||||
assert data[0][4] == 11
|
||||
assert data[0][5] == 10
|
||||
assert data[0][6] == ''
|
||||
|
||||
analysis.false_indicators.append('falseIndicator1')
|
||||
analysis.false_indicators.append('falseIndicator2')
|
||||
table, headers, data = (LookaheadAnalysisSubFunctions.
|
||||
text_table_lookahead_analysis_instances(lookahead_conf, [instance]))
|
||||
|
||||
assert data[0][6] == 'falseIndicator1, falseIndicator2'
|
||||
|
||||
# check amount of returning rows
|
||||
assert len(data) == 1
|
||||
|
||||
# check amount of multiple rows
|
||||
table, headers, data = (LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances(
|
||||
lookahead_conf, [instance, instance, instance]))
|
||||
assert len(data) == 3
|
||||
|
||||
|
||||
def test_lookahead_helper_export_to_csv(lookahead_conf):
|
||||
import pandas as pd
|
||||
lookahead_conf['lookahead_analysis_exportfilename'] = "temp_csv_lookahead_analysis.csv"
|
||||
|
||||
# just to be sure the test won't fail: remove file if exists for some reason
|
||||
# (repeat this at the end once again to clean up)
|
||||
if Path(lookahead_conf['lookahead_analysis_exportfilename']).exists():
|
||||
Path(lookahead_conf['lookahead_analysis_exportfilename']).unlink()
|
||||
|
||||
# before we can start we have to delete the
|
||||
|
||||
# 1st check: create a new file and verify its contents
|
||||
analysis1 = Analysis()
|
||||
analysis1.has_bias = True
|
||||
analysis1.total_signals = 12
|
||||
analysis1.false_entry_signals = 11
|
||||
analysis1.false_exit_signals = 10
|
||||
analysis1.false_indicators.append('falseIndicator1')
|
||||
analysis1.false_indicators.append('falseIndicator2')
|
||||
lookahead_conf['lookahead_analysis_exportfilename'] = "temp_csv_lookahead_analysis.csv"
|
||||
|
||||
strategy_obj1 = {
|
||||
'name': "strat1",
|
||||
'location': Path("file1.py"),
|
||||
}
|
||||
|
||||
instance1 = LookaheadAnalysis(lookahead_conf, strategy_obj1)
|
||||
instance1.failed_bias_check = False
|
||||
instance1.current_analysis = analysis1
|
||||
|
||||
LookaheadAnalysisSubFunctions.export_to_csv(lookahead_conf, [instance1])
|
||||
saved_data1 = pd.read_csv(lookahead_conf['lookahead_analysis_exportfilename'])
|
||||
|
||||
expected_values1 = [
|
||||
[
|
||||
'file1.py', 'strat1', True,
|
||||
12, 11, 10,
|
||||
"falseIndicator1,falseIndicator2"
|
||||
],
|
||||
]
|
||||
expected_columns = ['filename', 'strategy', 'has_bias',
|
||||
'total_signals', 'biased_entry_signals', 'biased_exit_signals',
|
||||
'biased_indicators']
|
||||
expected_data1 = pd.DataFrame(expected_values1, columns=expected_columns)
|
||||
|
||||
assert Path(lookahead_conf['lookahead_analysis_exportfilename']).exists()
|
||||
assert expected_data1.equals(saved_data1)
|
||||
|
||||
# 2nd check: update the same strategy (which internally changed or is being retested)
|
||||
expected_values2 = [
|
||||
[
|
||||
'file1.py', 'strat1', False,
|
||||
22, 21, 20,
|
||||
"falseIndicator3,falseIndicator4"
|
||||
],
|
||||
]
|
||||
expected_data2 = pd.DataFrame(expected_values2, columns=expected_columns)
|
||||
|
||||
analysis2 = Analysis()
|
||||
analysis2.has_bias = False
|
||||
analysis2.total_signals = 22
|
||||
analysis2.false_entry_signals = 21
|
||||
analysis2.false_exit_signals = 20
|
||||
analysis2.false_indicators.append('falseIndicator3')
|
||||
analysis2.false_indicators.append('falseIndicator4')
|
||||
|
||||
strategy_obj2 = {
|
||||
'name': "strat1",
|
||||
'location': Path("file1.py"),
|
||||
}
|
||||
|
||||
instance2 = LookaheadAnalysis(lookahead_conf, strategy_obj2)
|
||||
instance2.failed_bias_check = False
|
||||
instance2.current_analysis = analysis2
|
||||
|
||||
LookaheadAnalysisSubFunctions.export_to_csv(lookahead_conf, [instance2])
|
||||
saved_data2 = pd.read_csv(lookahead_conf['lookahead_analysis_exportfilename'])
|
||||
|
||||
assert expected_data2.equals(saved_data2)
|
||||
|
||||
# 3rd check: now we add a new row to an already existing file
|
||||
expected_values3 = [
|
||||
[
|
||||
'file1.py', 'strat1', False,
|
||||
22, 21, 20,
|
||||
"falseIndicator3,falseIndicator4"
|
||||
],
|
||||
[
|
||||
'file3.py', 'strat3', True,
|
||||
32, 31, 30, "falseIndicator5,falseIndicator6"
|
||||
],
|
||||
]
|
||||
|
||||
expected_data3 = pd.DataFrame(expected_values3, columns=expected_columns)
|
||||
|
||||
analysis3 = Analysis()
|
||||
analysis3.has_bias = True
|
||||
analysis3.total_signals = 32
|
||||
analysis3.false_entry_signals = 31
|
||||
analysis3.false_exit_signals = 30
|
||||
analysis3.false_indicators.append('falseIndicator5')
|
||||
analysis3.false_indicators.append('falseIndicator6')
|
||||
lookahead_conf['lookahead_analysis_exportfilename'] = "temp_csv_lookahead_analysis.csv"
|
||||
|
||||
strategy_obj3 = {
|
||||
'name': "strat3",
|
||||
'location': Path("file3.py"),
|
||||
}
|
||||
|
||||
instance3 = LookaheadAnalysis(lookahead_conf, strategy_obj3)
|
||||
instance3.failed_bias_check = False
|
||||
instance3.current_analysis = analysis3
|
||||
|
||||
LookaheadAnalysisSubFunctions.export_to_csv(lookahead_conf, [instance3])
|
||||
saved_data3 = pd.read_csv(lookahead_conf['lookahead_analysis_exportfilename'])
|
||||
assert expected_data3.equals(saved_data3)
|
||||
|
||||
# remove csv file after the test is done
|
||||
if Path(lookahead_conf['lookahead_analysis_exportfilename']).exists():
|
||||
Path(lookahead_conf['lookahead_analysis_exportfilename']).unlink()
|
||||
|
||||
|
||||
def test_initialize_single_lookahead_analysis(lookahead_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.data.history.get_timerange', get_timerange)
|
||||
mocker.patch(f'{EXMS}.get_fee', return_value=0.0)
|
||||
mocker.patch(f'{EXMS}.get_min_pair_stake_amount', return_value=0.00001)
|
||||
mocker.patch(f'{EXMS}.get_max_pair_stake_amount', return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['UNITTEST/BTC']))
|
||||
lookahead_conf['pairs'] = ['UNITTEST/USDT']
|
||||
|
||||
lookahead_conf['timeframe'] = '5m'
|
||||
lookahead_conf['timerange'] = '20180119-20180122'
|
||||
start_mock = mocker.patch('freqtrade.optimize.lookahead_analysis.LookaheadAnalysis.start')
|
||||
strategy_obj = {
|
||||
'name': "strategy_test_v3_with_lookahead_bias",
|
||||
'location': Path(lookahead_conf['strategy_path'], f"{lookahead_conf['strategy']}.py")
|
||||
}
|
||||
|
||||
instance = LookaheadAnalysisSubFunctions.initialize_single_lookahead_analysis(
|
||||
lookahead_conf, strategy_obj)
|
||||
assert log_has_re(r"Bias test of .* started\.", caplog)
|
||||
assert start_mock.call_count == 1
|
||||
|
||||
assert instance.strategy_obj['name'] == "strategy_test_v3_with_lookahead_bias"
|
||||
|
||||
|
||||
@pytest.mark.parametrize('scenario', [
|
||||
'no_bias', 'bias1'
|
||||
])
|
||||
def test_biased_strategy(lookahead_conf, mocker, caplog, scenario) -> None:
|
||||
mocker.patch('freqtrade.data.history.get_timerange', get_timerange)
|
||||
mocker.patch(f'{EXMS}.get_fee', return_value=0.0)
|
||||
mocker.patch(f'{EXMS}.get_min_pair_stake_amount', return_value=0.00001)
|
||||
mocker.patch(f'{EXMS}.get_max_pair_stake_amount', return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['UNITTEST/BTC']))
|
||||
lookahead_conf['pairs'] = ['UNITTEST/USDT']
|
||||
|
||||
lookahead_conf['timeframe'] = '5m'
|
||||
lookahead_conf['timerange'] = '20180119-20180122'
|
||||
|
||||
# Patch scenario Parameter to allow for easy selection
|
||||
mocker.patch('freqtrade.strategy.hyper.HyperStrategyMixin.load_params_from_file',
|
||||
return_value={
|
||||
'params': {
|
||||
"buy": {
|
||||
"scenario": scenario
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
strategy_obj = {'name': "strategy_test_v3_with_lookahead_bias"}
|
||||
instance = LookaheadAnalysis(lookahead_conf, strategy_obj)
|
||||
instance.start()
|
||||
# Assert init correct
|
||||
assert log_has_re(f"Strategy Parameter: scenario = {scenario}", caplog)
|
||||
|
||||
# check non-biased strategy
|
||||
if scenario == "no_bias":
|
||||
assert not instance.current_analysis.has_bias
|
||||
# check biased strategy
|
||||
elif scenario == "bias1":
|
||||
assert instance.current_analysis.has_bias
|
||||
|
||||
|
||||
def test_config_overrides(lookahead_conf):
|
||||
lookahead_conf['max_open_trades'] = 0
|
||||
lookahead_conf['dry_run_wallet'] = 1
|
||||
lookahead_conf['pairs'] = ['BTC/USDT', 'ETH/USDT', 'SOL/USDT']
|
||||
lookahead_conf = LookaheadAnalysisSubFunctions.calculate_config_overrides(lookahead_conf)
|
||||
|
||||
assert lookahead_conf['dry_run_wallet'] == 1000000000
|
||||
assert lookahead_conf['max_open_trades'] == 3
|
||||
@@ -0,0 +1,58 @@
|
||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
from pandas import DataFrame
|
||||
from technical.indicators import ichimoku
|
||||
|
||||
from freqtrade.strategy import IStrategy
|
||||
from freqtrade.strategy.parameters import CategoricalParameter
|
||||
|
||||
|
||||
class strategy_test_v3_with_lookahead_bias(IStrategy):
|
||||
INTERFACE_VERSION = 3
|
||||
|
||||
# Minimal ROI designed for the strategy
|
||||
minimal_roi = {
|
||||
"40": 0.0,
|
||||
"30": 0.01,
|
||||
"20": 0.02,
|
||||
"0": 0.04
|
||||
}
|
||||
|
||||
# Optimal stoploss designed for the strategy
|
||||
stoploss = -0.10
|
||||
|
||||
# Optimal timeframe for the strategy
|
||||
timeframe = '5m'
|
||||
scenario = CategoricalParameter(['no_bias', 'bias1'], default='bias1', space="buy")
|
||||
|
||||
# Number of candles the strategy requires before producing valid signals
|
||||
startup_candle_count: int = 20
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
# bias is introduced here
|
||||
if self.scenario.value != 'no_bias':
|
||||
ichi = ichimoku(dataframe,
|
||||
conversion_line_period=20,
|
||||
base_line_periods=60,
|
||||
laggin_span=120,
|
||||
displacement=30)
|
||||
dataframe['chikou_span'] = ichi['chikou_span']
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
if self.scenario.value == 'no_bias':
|
||||
dataframe.loc[dataframe['close'].shift(10) < dataframe['close'], 'enter_long'] = 1
|
||||
else:
|
||||
dataframe.loc[dataframe['close'].shift(-10) > dataframe['close'], 'enter_long'] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
if self.scenario.value == 'no_bias':
|
||||
dataframe.loc[
|
||||
dataframe['close'].shift(10) < dataframe['close'], 'exit'] = 1
|
||||
else:
|
||||
dataframe.loc[
|
||||
dataframe['close'].shift(-10) > dataframe['close'], 'exit'] = 1
|
||||
|
||||
return dataframe
|
||||
@@ -1237,6 +1237,8 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
|
||||
'type': 'stop_loss_limit',
|
||||
'price': 3,
|
||||
'average': 2,
|
||||
'filled': enter_order['amount'],
|
||||
'remaining': 0,
|
||||
'amount': enter_order['amount'],
|
||||
})
|
||||
mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hit)
|
||||
@@ -3029,8 +3031,8 @@ def test_manage_open_orders_exit_usercustom(
|
||||
freqtrade.manage_open_orders()
|
||||
assert cancel_order_mock.call_count == 0
|
||||
assert rpc_mock.call_count == 1
|
||||
assert freqtrade.strategy.check_exit_timeout.call_count == 1
|
||||
assert freqtrade.strategy.check_entry_timeout.call_count == 0
|
||||
assert freqtrade.strategy.check_exit_timeout.call_count == (0 if is_short else 1)
|
||||
assert freqtrade.strategy.check_entry_timeout.call_count == (1 if is_short else 0)
|
||||
|
||||
freqtrade.strategy.check_exit_timeout = MagicMock(side_effect=KeyError)
|
||||
freqtrade.strategy.check_entry_timeout = MagicMock(side_effect=KeyError)
|
||||
@@ -3038,8 +3040,8 @@ def test_manage_open_orders_exit_usercustom(
|
||||
freqtrade.manage_open_orders()
|
||||
assert cancel_order_mock.call_count == 0
|
||||
assert rpc_mock.call_count == 1
|
||||
assert freqtrade.strategy.check_exit_timeout.call_count == 1
|
||||
assert freqtrade.strategy.check_entry_timeout.call_count == 0
|
||||
assert freqtrade.strategy.check_exit_timeout.call_count == (0 if is_short else 1)
|
||||
assert freqtrade.strategy.check_entry_timeout.call_count == (1 if is_short else 0)
|
||||
|
||||
# Return True - sells!
|
||||
freqtrade.strategy.check_exit_timeout = MagicMock(return_value=True)
|
||||
@@ -3047,8 +3049,8 @@ def test_manage_open_orders_exit_usercustom(
|
||||
freqtrade.manage_open_orders()
|
||||
assert cancel_order_mock.call_count == 1
|
||||
assert rpc_mock.call_count == 2
|
||||
assert freqtrade.strategy.check_exit_timeout.call_count == 1
|
||||
assert freqtrade.strategy.check_entry_timeout.call_count == 0
|
||||
assert freqtrade.strategy.check_exit_timeout.call_count == (0 if is_short else 1)
|
||||
assert freqtrade.strategy.check_entry_timeout.call_count == (1 if is_short else 0)
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
# cancelling didn't succeed - order-id remains open.
|
||||
assert trade.open_order_id is not None
|
||||
|
||||
@@ -7,6 +7,8 @@ import pytest
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.loggers import (FTBufferingHandler, FTStdErrStreamHandler, set_loggers,
|
||||
setup_logging, setup_logging_pre)
|
||||
from freqtrade.loggers.set_log_levels import (reduce_verbosity_for_bias_tester,
|
||||
restore_verbosity_for_bias_tester)
|
||||
|
||||
|
||||
def test_set_loggers() -> None:
|
||||
@@ -128,3 +130,21 @@ def test_set_loggers_journald_importerror(import_fails):
|
||||
match=r'You need the cysystemd python package.*'):
|
||||
setup_logging(config)
|
||||
logger.handlers = orig_handlers
|
||||
|
||||
|
||||
def test_reduce_verbosity():
|
||||
setup_logging_pre()
|
||||
reduce_verbosity_for_bias_tester()
|
||||
prior_level = logging.getLogger('freqtrade').getEffectiveLevel()
|
||||
|
||||
assert logging.getLogger('freqtrade.resolvers').getEffectiveLevel() == logging.WARNING
|
||||
assert logging.getLogger('freqtrade.strategy.hyper').getEffectiveLevel() == logging.WARNING
|
||||
# base level wasn't changed
|
||||
assert logging.getLogger('freqtrade').getEffectiveLevel() == prior_level
|
||||
|
||||
restore_verbosity_for_bias_tester()
|
||||
|
||||
assert logging.getLogger('freqtrade.resolvers').getEffectiveLevel() == prior_level
|
||||
assert logging.getLogger('freqtrade.strategy.hyper').getEffectiveLevel() == prior_level
|
||||
assert logging.getLogger('freqtrade').getEffectiveLevel() == prior_level
|
||||
# base level wasn't changed
|
||||
|
||||
Reference in New Issue
Block a user