mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-03-06 13:53:13 +00:00
Merge branch 'freqtrade:develop' into allow-pairs-with-prefix-in-marketcap-pairList
This commit is contained in:
@@ -1862,8 +1862,10 @@ def test_backtesting_show(mocker, testdatadir, capsys):
|
||||
sbr = mocker.patch("freqtrade.optimize.optimize_reports.show_backtest_results")
|
||||
args = [
|
||||
"backtesting-show",
|
||||
"--export-directory",
|
||||
f"{testdatadir / 'backtest_results'}",
|
||||
"--export-filename",
|
||||
f"{testdatadir / 'backtest_results/backtest-result.json'}",
|
||||
"backtest-result.json",
|
||||
"--show-pair-list",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
|
||||
@@ -521,7 +521,11 @@ def patch_torch_initlogs(mocker) -> None:
|
||||
mocked_module = types.ModuleType(module_name)
|
||||
sys.modules[module_name] = mocked_module
|
||||
else:
|
||||
mocker.patch("torch._logging._init_logs")
|
||||
try:
|
||||
mocker.patch("torch._logging._init_logs")
|
||||
except ModuleNotFoundError:
|
||||
# Allow running limited tests to run without freqAI dependencies
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
122
tests/exchange/test_bitget.py
Normal file
122
tests/exchange/test_bitget.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.enums import CandleType
|
||||
from freqtrade.exceptions import RetryableOrderError
|
||||
from freqtrade.exchange.common import API_RETRY_COUNT
|
||||
from freqtrade.util import dt_now, dt_ts
|
||||
from tests.conftest import EXMS, get_patched_exchange
|
||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_fetch_stoploss_order_bitget(default_conf, mocker):
|
||||
default_conf["dry_run"] = False
|
||||
mocker.patch("freqtrade.exchange.common.time.sleep")
|
||||
api_mock = MagicMock()
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget")
|
||||
|
||||
api_mock.fetch_open_orders = MagicMock(return_value=[])
|
||||
api_mock.fetch_canceled_and_closed_orders = MagicMock(return_value=[])
|
||||
|
||||
with pytest.raises(RetryableOrderError):
|
||||
exchange.fetch_stoploss_order("1234", "ETH/BTC")
|
||||
assert api_mock.fetch_open_orders.call_count == API_RETRY_COUNT + 1
|
||||
assert api_mock.fetch_canceled_and_closed_orders.call_count == API_RETRY_COUNT + 1
|
||||
|
||||
api_mock.fetch_open_orders.reset_mock()
|
||||
api_mock.fetch_canceled_and_closed_orders.reset_mock()
|
||||
|
||||
api_mock.fetch_canceled_and_closed_orders = MagicMock(
|
||||
return_value=[{"id": "1234", "status": "closed", "clientOrderId": "123455"}]
|
||||
)
|
||||
api_mock.fetch_open_orders = MagicMock(return_value=[{"id": "50110", "clientOrderId": "1234"}])
|
||||
|
||||
resp = exchange.fetch_stoploss_order("1234", "ETH/BTC")
|
||||
assert api_mock.fetch_open_orders.call_count == 2
|
||||
assert api_mock.fetch_canceled_and_closed_orders.call_count == 2
|
||||
|
||||
assert resp["id"] == "1234"
|
||||
assert resp["id_stop"] == "50110"
|
||||
assert resp["type"] == "stoploss"
|
||||
|
||||
default_conf["dry_run"] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget")
|
||||
dro_mock = mocker.patch(f"{EXMS}.fetch_dry_run_order", MagicMock(return_value={"id": "123455"}))
|
||||
|
||||
api_mock.fetch_open_orders.reset_mock()
|
||||
api_mock.fetch_canceled_and_closed_orders.reset_mock()
|
||||
resp = exchange.fetch_stoploss_order("1234", "ETH/BTC")
|
||||
|
||||
assert api_mock.fetch_open_orders.call_count == 0
|
||||
assert api_mock.fetch_canceled_and_closed_orders.call_count == 0
|
||||
assert dro_mock.call_count == 1
|
||||
|
||||
|
||||
def test_fetch_stoploss_order_bitget_exceptions(default_conf_usdt, mocker):
|
||||
default_conf_usdt["dry_run"] = False
|
||||
api_mock = MagicMock()
|
||||
|
||||
# Test emulation of the stoploss getters
|
||||
api_mock.fetch_canceled_and_closed_orders = MagicMock(return_value=[])
|
||||
|
||||
ccxt_exceptionhandlers(
|
||||
mocker,
|
||||
default_conf_usdt,
|
||||
api_mock,
|
||||
"bitget",
|
||||
"fetch_stoploss_order",
|
||||
"fetch_open_orders",
|
||||
retries=API_RETRY_COUNT + 1,
|
||||
order_id="12345",
|
||||
pair="ETH/USDT",
|
||||
)
|
||||
|
||||
|
||||
def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt):
|
||||
# This test is also a live test - so we're sure our limits are correct.
|
||||
api_mock = MagicMock()
|
||||
api_mock.options = {
|
||||
"fetchOHLCV": {
|
||||
"maxRecentDaysPerTimeframe": {
|
||||
"1m": 30,
|
||||
"5m": 30,
|
||||
"15m": 30,
|
||||
"30m": 30,
|
||||
"1h": 60,
|
||||
"4h": 60,
|
||||
"1d": 60,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exch = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="bitget")
|
||||
timeframes = ("1m", "5m", "1h")
|
||||
|
||||
for timeframe in timeframes:
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 200
|
||||
|
||||
start_time = dt_ts(dt_now() - timedelta(days=17))
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200
|
||||
start_time = dt_ts(dt_now() - timedelta(days=48))
|
||||
length = 200 if timeframe in ("1m", "5m") else 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200
|
||||
|
||||
start_time = dt_ts(dt_now() - timedelta(days=61))
|
||||
length = 200
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200
|
||||
@@ -541,24 +541,24 @@ class TestCCXTExchange:
|
||||
for timeframe in timeframes:
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 200
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 200
|
||||
|
||||
start_time = dt_ts(dt_now() - timedelta(days=17))
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200
|
||||
start_time = dt_ts(dt_now() - timedelta(days=48))
|
||||
length = 200 if timeframe in ("1m", "5m") else 1000
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200
|
||||
|
||||
start_time = dt_ts(dt_now() - timedelta(days=61))
|
||||
length = 200
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length
|
||||
assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200
|
||||
|
||||
@@ -1528,6 +1528,25 @@ def test_handle_trade(
|
||||
assert trade.close_date is not None
|
||||
assert trade.exit_reason == "sell_signal1"
|
||||
|
||||
correct_profit_ratio = trade.calc_profit_ratio(
|
||||
rate=trade.close_rate, amount=trade.amount, open_rate=trade.open_rate
|
||||
)
|
||||
profit_ratio_1 = trade.calc_profit_ratio(rate=trade.close_rate, open_rate=trade.open_rate)
|
||||
profit_ratio_2 = trade.calc_profit_ratio(
|
||||
rate=trade.close_rate, open_rate=trade.open_rate * 1.02
|
||||
)
|
||||
profit_ratio_3 = trade.calc_profit_ratio(rate=trade.close_rate, amount=trade.amount)
|
||||
profit_ratio_4 = trade.calc_profit_ratio(rate=trade.close_rate)
|
||||
profit_ratio_5 = trade.calc_profit_ratio(
|
||||
rate=trade.close_rate, amount=trade.amount, open_rate=trade.open_rate * 1.02
|
||||
)
|
||||
assert correct_profit_ratio == close_profit
|
||||
assert correct_profit_ratio == profit_ratio_1
|
||||
assert correct_profit_ratio != profit_ratio_2
|
||||
assert correct_profit_ratio == profit_ratio_3
|
||||
assert correct_profit_ratio == profit_ratio_4
|
||||
assert correct_profit_ratio != profit_ratio_5
|
||||
|
||||
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_handle_overlapping_signals(
|
||||
@@ -5729,7 +5748,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"data",
|
||||
[
|
||||
# tuple 1 - side amount, price
|
||||
# tuple 1 - side, amount, price
|
||||
# tuple 2 - amount, open_rate, stake_amount, cumulative_profit, realized_profit, rel_profit
|
||||
(
|
||||
(("buy", 100, 10), (100.0, 10.0, 1000.0, 0.0, None, None)),
|
||||
|
||||
@@ -236,7 +236,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path):
|
||||
filename_last = tmp_path / LAST_BT_RESULT_FN
|
||||
_backup_file(filename_last, copy_file=True)
|
||||
assert not filename.is_file()
|
||||
default_conf["exportfilename"] = filename
|
||||
default_conf["exportdirectory"] = filename
|
||||
|
||||
store_backtest_results(default_conf, stats, "2022_01_01_15_05_13")
|
||||
|
||||
@@ -263,7 +263,7 @@ def test_store_backtest_results(testdatadir, mocker):
|
||||
zip_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.ZipFile")
|
||||
data = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
|
||||
store_backtest_results(
|
||||
{"exportfilename": testdatadir, "original_config": {}}, data, "2022_01_01_15_05_13"
|
||||
{"exportdirectory": testdatadir, "original_config": {}}, data, "2022_01_01_15_05_13"
|
||||
)
|
||||
|
||||
assert dump_mock.call_count == 2
|
||||
@@ -275,7 +275,7 @@ def test_store_backtest_results(testdatadir, mocker):
|
||||
zip_mock.reset_mock()
|
||||
filename = testdatadir / "testresult.json"
|
||||
store_backtest_results(
|
||||
{"exportfilename": filename, "original_config": {}}, data, "2022_01_01_15_05_13"
|
||||
{"exportdirectory": filename, "original_config": {}}, data, "2022_01_01_15_05_13"
|
||||
)
|
||||
assert dump_mock.call_count == 2
|
||||
assert zip_mock.call_count == 1
|
||||
@@ -287,7 +287,7 @@ def test_store_backtest_results(testdatadir, mocker):
|
||||
def test_store_backtest_results_real(tmp_path, caplog):
|
||||
data = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
|
||||
config = {
|
||||
"exportfilename": tmp_path,
|
||||
"exportdirectory": tmp_path,
|
||||
"original_config": {},
|
||||
}
|
||||
store_backtest_results(
|
||||
@@ -356,7 +356,7 @@ def test_write_read_backtest_candles(tmp_path):
|
||||
bt_results = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
|
||||
|
||||
mock_conf = {
|
||||
"exportfilename": tmp_path,
|
||||
"exportdirectory": tmp_path,
|
||||
"export": "signals",
|
||||
"runmode": "backtest",
|
||||
"original_config": {},
|
||||
@@ -393,33 +393,6 @@ def test_write_read_backtest_candles(tmp_path):
|
||||
|
||||
_clean_test_file(stored_file)
|
||||
|
||||
# test file exporting
|
||||
filename = tmp_path / "testresult"
|
||||
mock_conf["exportfilename"] = filename
|
||||
store_backtest_results(mock_conf, bt_results, sample_date, analysis_results=data)
|
||||
stored_file = tmp_path / f"testresult-{sample_date}.zip"
|
||||
signals_pkl = f"testresult-{sample_date}_signals.pkl"
|
||||
rejected_pkl = f"testresult-{sample_date}_rejected.pkl"
|
||||
exited_pkl = f"testresult-{sample_date}_exited.pkl"
|
||||
assert not (tmp_path / signals_pkl).is_file()
|
||||
assert stored_file.is_file()
|
||||
|
||||
with ZipFile(stored_file, "r") as zipf:
|
||||
assert signals_pkl in zipf.namelist()
|
||||
assert rejected_pkl in zipf.namelist()
|
||||
assert exited_pkl in zipf.namelist()
|
||||
|
||||
with zipf.open(signals_pkl) as scp:
|
||||
pickled_signal_candles2 = joblib.load(scp)
|
||||
|
||||
assert pickled_signal_candles2.keys() == candle_dict.keys()
|
||||
assert pickled_signal_candles2["DefStrat"].keys() == pickled_signal_candles2["DefStrat"].keys()
|
||||
assert pickled_signal_candles2["DefStrat"]["UNITTEST/BTC"].equals(
|
||||
pickled_signal_candles2["DefStrat"]["UNITTEST/BTC"]
|
||||
)
|
||||
|
||||
_clean_test_file(stored_file)
|
||||
|
||||
|
||||
def test_generate_pair_metrics():
|
||||
results = pd.DataFrame(
|
||||
|
||||
@@ -1883,7 +1883,12 @@ def test_pairlistmanager_no_pairlist(mocker, whitelist_conf):
|
||||
|
||||
whitelist_conf["pairlists"] = []
|
||||
|
||||
with pytest.raises(OperationalException, match=r"No Pairlist Handlers defined"):
|
||||
with pytest.raises(OperationalException, match=r"\[\] should be non-empty"):
|
||||
get_patched_freqtradebot(mocker, whitelist_conf)
|
||||
|
||||
del whitelist_conf["pairlists"]
|
||||
|
||||
with pytest.raises(OperationalException, match=r"'pairlists' is a required property"):
|
||||
get_patched_freqtradebot(mocker, whitelist_conf)
|
||||
|
||||
|
||||
|
||||
@@ -2802,8 +2802,8 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmp_path):
|
||||
ftbot.config["export"] = "trades"
|
||||
ftbot.config["backtest_cache"] = "day"
|
||||
ftbot.config["user_data_dir"] = tmp_path
|
||||
ftbot.config["exportfilename"] = tmp_path / "backtest_results"
|
||||
ftbot.config["exportfilename"].mkdir()
|
||||
ftbot.config["exportdirectory"] = tmp_path / "backtest_results"
|
||||
ftbot.config["exportdirectory"].mkdir()
|
||||
|
||||
# start backtesting
|
||||
data = {
|
||||
|
||||
@@ -12,20 +12,13 @@ from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import CUSTOM_TAG_MAX_LENGTH
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.data.history import load_data
|
||||
from freqtrade.enums import ExitCheckTuple, ExitType, HyperoptState, SignalDirection
|
||||
from freqtrade.enums import ExitCheckTuple, ExitType, SignalDirection
|
||||
from freqtrade.exceptions import OperationalException, StrategyError
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer
|
||||
from freqtrade.optimize.space import SKDecimal
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
from freqtrade.strategy.hyper import detect_parameters
|
||||
from freqtrade.strategy.parameters import (
|
||||
BaseParameter,
|
||||
BooleanParameter,
|
||||
CategoricalParameter,
|
||||
DecimalParameter,
|
||||
IntParameter,
|
||||
RealParameter,
|
||||
)
|
||||
from freqtrade.strategy.strategy_validation import StrategyResultValidator
|
||||
from freqtrade.util import dt_now
|
||||
@@ -930,95 +923,6 @@ def test_is_informative_pairs_callback(default_conf):
|
||||
assert [] == strategy.gather_informative_pairs()
|
||||
|
||||
|
||||
def test_hyperopt_parameters():
|
||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||
from optuna.distributions import CategoricalDistribution, FloatDistribution, IntDistribution
|
||||
|
||||
with pytest.raises(OperationalException, match=r"Name is determined.*"):
|
||||
IntParameter(low=0, high=5, default=1, name="hello")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"IntParameter space must be.*"):
|
||||
IntParameter(low=0, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"RealParameter space must be.*"):
|
||||
RealParameter(low=0, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"DecimalParameter space must be.*"):
|
||||
DecimalParameter(low=0, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"IntParameter space invalid\."):
|
||||
IntParameter([0, 10], high=7, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"RealParameter space invalid\."):
|
||||
RealParameter([0, 10], high=7, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"DecimalParameter space invalid\."):
|
||||
DecimalParameter([0, 10], high=7, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"CategoricalParameter space must.*"):
|
||||
CategoricalParameter(["aa"], default="aa", space="buy")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
BaseParameter(opt_range=[0, 1], default=1, space="buy")
|
||||
|
||||
intpar = IntParameter(low=0, high=5, default=1, space="buy")
|
||||
assert intpar.value == 1
|
||||
assert isinstance(intpar.get_space(""), IntDistribution)
|
||||
assert isinstance(intpar.range, range)
|
||||
assert len(list(intpar.range)) == 1
|
||||
# Range contains ONLY the default / value.
|
||||
assert list(intpar.range) == [intpar.value]
|
||||
intpar.in_space = True
|
||||
|
||||
assert len(list(intpar.range)) == 6
|
||||
assert list(intpar.range) == [0, 1, 2, 3, 4, 5]
|
||||
|
||||
fltpar = RealParameter(low=0.0, high=5.5, default=1.0, space="buy")
|
||||
assert fltpar.value == 1
|
||||
assert isinstance(fltpar.get_space(""), FloatDistribution)
|
||||
|
||||
fltpar = DecimalParameter(low=0.0, high=0.5, default=0.14, decimals=1, space="buy")
|
||||
assert fltpar.value == 0.1
|
||||
assert isinstance(fltpar.get_space(""), SKDecimal)
|
||||
assert isinstance(fltpar.range, list)
|
||||
assert len(list(fltpar.range)) == 1
|
||||
# Range contains ONLY the default / value.
|
||||
assert list(fltpar.range) == [fltpar.value]
|
||||
fltpar.in_space = True
|
||||
assert len(list(fltpar.range)) == 6
|
||||
assert list(fltpar.range) == [0.0, 0.1, 0.2, 0.3, 0.4, 0.5]
|
||||
|
||||
catpar = CategoricalParameter(
|
||||
["buy_rsi", "buy_macd", "buy_none"], default="buy_macd", space="buy"
|
||||
)
|
||||
assert catpar.value == "buy_macd"
|
||||
assert isinstance(catpar.get_space(""), CategoricalDistribution)
|
||||
assert isinstance(catpar.range, list)
|
||||
assert len(list(catpar.range)) == 1
|
||||
# Range contains ONLY the default / value.
|
||||
assert list(catpar.range) == [catpar.value]
|
||||
catpar.in_space = True
|
||||
assert len(list(catpar.range)) == 3
|
||||
assert list(catpar.range) == ["buy_rsi", "buy_macd", "buy_none"]
|
||||
|
||||
boolpar = BooleanParameter(default=True, space="buy")
|
||||
assert boolpar.value is True
|
||||
assert isinstance(boolpar.get_space(""), CategoricalDistribution)
|
||||
assert isinstance(boolpar.range, list)
|
||||
assert len(list(boolpar.range)) == 1
|
||||
|
||||
boolpar.in_space = True
|
||||
assert len(list(boolpar.range)) == 2
|
||||
|
||||
assert list(boolpar.range) == [True, False]
|
||||
|
||||
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
|
||||
assert len(list(intpar.range)) == 1
|
||||
assert len(list(fltpar.range)) == 1
|
||||
assert len(list(catpar.range)) == 1
|
||||
assert len(list(boolpar.range)) == 1
|
||||
|
||||
|
||||
def test_auto_hyperopt_interface(default_conf):
|
||||
default_conf.update({"strategy": "HyperoptableStrategyV2"})
|
||||
PairLocks.timeframe = default_conf["timeframe"]
|
||||
|
||||
136
tests/strategy/test_strategy_parameters.py
Normal file
136
tests/strategy/test_strategy_parameters.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.enums import HyperoptState
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer
|
||||
from freqtrade.strategy.parameters import (
|
||||
BaseParameter,
|
||||
BooleanParameter,
|
||||
CategoricalParameter,
|
||||
DecimalParameter,
|
||||
IntParameter,
|
||||
RealParameter,
|
||||
)
|
||||
|
||||
|
||||
def test_hyperopt_int_parameter():
|
||||
from optuna.distributions import IntDistribution
|
||||
|
||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||
|
||||
with pytest.raises(OperationalException, match=r"Name is determined.*"):
|
||||
IntParameter(low=0, high=5, default=1, name="hello")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"IntParameter space must be.*"):
|
||||
IntParameter(low=0, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"IntParameter space invalid\."):
|
||||
IntParameter([0, 10], high=7, default=5, space="buy")
|
||||
|
||||
intpar = IntParameter(low=0, high=5, default=1, space="buy")
|
||||
assert intpar.value == 1
|
||||
assert isinstance(intpar.get_space(""), IntDistribution)
|
||||
assert isinstance(intpar.range, range)
|
||||
assert len(list(intpar.range)) == 1
|
||||
# Range contains ONLY the default / value.
|
||||
assert list(intpar.range) == [intpar.value]
|
||||
intpar.in_space = True
|
||||
|
||||
assert len(list(intpar.range)) == 6
|
||||
assert list(intpar.range) == [0, 1, 2, 3, 4, 5]
|
||||
|
||||
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
|
||||
assert len(list(intpar.range)) == 1
|
||||
|
||||
|
||||
def test_hyperopt_real_parameter():
|
||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||
from optuna.distributions import FloatDistribution
|
||||
|
||||
with pytest.raises(OperationalException, match=r"RealParameter space must be.*"):
|
||||
RealParameter(low=0, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"RealParameter space invalid\."):
|
||||
RealParameter([0, 10], high=7, default=5, space="buy")
|
||||
|
||||
fltpar = RealParameter(low=0.0, high=5.5, default=1.0, space="buy")
|
||||
assert fltpar.value == 1.0
|
||||
assert isinstance(fltpar.get_space(""), FloatDistribution)
|
||||
|
||||
assert not hasattr(fltpar, "range")
|
||||
|
||||
|
||||
def test_hyperopt_decimal_parameter():
|
||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||
# TODO: Check for get_space??
|
||||
from freqtrade.optimize.space import SKDecimal
|
||||
|
||||
with pytest.raises(OperationalException, match=r"DecimalParameter space must be.*"):
|
||||
DecimalParameter(low=0, default=5, space="buy")
|
||||
|
||||
with pytest.raises(OperationalException, match=r"DecimalParameter space invalid\."):
|
||||
DecimalParameter([0, 10], high=7, default=5, space="buy")
|
||||
|
||||
decimalpar = DecimalParameter(low=0.0, high=0.5, default=0.14, decimals=1, space="buy")
|
||||
assert decimalpar.value == 0.1
|
||||
assert isinstance(decimalpar.get_space(""), SKDecimal)
|
||||
assert isinstance(decimalpar.range, list)
|
||||
assert len(list(decimalpar.range)) == 1
|
||||
# Range contains ONLY the default / value.
|
||||
assert list(decimalpar.range) == [decimalpar.value]
|
||||
decimalpar.in_space = True
|
||||
assert len(list(decimalpar.range)) == 6
|
||||
assert list(decimalpar.range) == [0.0, 0.1, 0.2, 0.3, 0.4, 0.5]
|
||||
|
||||
decimalpar2 = DecimalParameter(low=0.01, high=0.03, decimals=3, default=0.02, space="buy")
|
||||
decimalpar2.in_space = True
|
||||
assert len(list(decimalpar2.range)) == 21
|
||||
expected_range = [round(0.01 + i * 0.001, 3) for i in range(21)]
|
||||
assert list(decimalpar2.range) == expected_range
|
||||
assert decimalpar2.value == 0.02
|
||||
decimalpar2.value = 0.022222
|
||||
assert decimalpar2.value == 0.022
|
||||
|
||||
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
|
||||
assert len(list(decimalpar.range)) == 1
|
||||
|
||||
|
||||
def test_hyperopt_categorical_parameter():
|
||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||
from optuna.distributions import CategoricalDistribution
|
||||
|
||||
with pytest.raises(OperationalException, match=r"CategoricalParameter space must.*"):
|
||||
CategoricalParameter(["aa"], default="aa", space="buy")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
BaseParameter(opt_range=[0, 1], default=1, space="buy")
|
||||
|
||||
catpar = CategoricalParameter(
|
||||
["buy_rsi", "buy_macd", "buy_none"], default="buy_macd", space="buy"
|
||||
)
|
||||
assert catpar.value == "buy_macd"
|
||||
assert isinstance(catpar.get_space(""), CategoricalDistribution)
|
||||
assert isinstance(catpar.range, list)
|
||||
assert len(list(catpar.range)) == 1
|
||||
# Range contains ONLY the default / value.
|
||||
assert list(catpar.range) == [catpar.value]
|
||||
catpar.in_space = True
|
||||
assert len(list(catpar.range)) == 3
|
||||
assert list(catpar.range) == ["buy_rsi", "buy_macd", "buy_none"]
|
||||
|
||||
boolpar = BooleanParameter(default=True, space="buy")
|
||||
assert boolpar.value is True
|
||||
assert isinstance(boolpar.get_space(""), CategoricalDistribution)
|
||||
assert isinstance(boolpar.range, list)
|
||||
assert len(list(boolpar.range)) == 1
|
||||
|
||||
boolpar.in_space = True
|
||||
assert len(list(boolpar.range)) == 2
|
||||
|
||||
assert list(boolpar.range) == [True, False]
|
||||
|
||||
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
|
||||
assert len(list(catpar.range)) == 1
|
||||
assert len(list(boolpar.range)) == 1
|
||||
@@ -6,7 +6,6 @@ from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from jsonschema import ValidationError
|
||||
|
||||
from freqtrade.commands import Arguments
|
||||
from freqtrade.configuration import (
|
||||
@@ -51,20 +50,20 @@ def test_load_config_missing_attributes(default_conf) -> None:
|
||||
conf = deepcopy(default_conf)
|
||||
conf.pop("exchange")
|
||||
|
||||
with pytest.raises(ValidationError, match=r".*'exchange' is a required property.*"):
|
||||
with pytest.raises(ConfigurationError, match=r".*'exchange' is a required property.*"):
|
||||
validate_config_schema(conf)
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf.pop("stake_currency")
|
||||
conf["runmode"] = RunMode.DRY_RUN
|
||||
with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"):
|
||||
with pytest.raises(ConfigurationError, match=r".*'stake_currency' is a required property.*"):
|
||||
validate_config_schema(conf)
|
||||
|
||||
|
||||
def test_load_config_incorrect_stake_amount(default_conf) -> None:
|
||||
default_conf["stake_amount"] = "fake"
|
||||
|
||||
with pytest.raises(ValidationError, match=r".*'fake' does not match 'unlimited'.*"):
|
||||
with pytest.raises(ConfigurationError, match=r".*'fake' does not match 'unlimited'.*"):
|
||||
validate_config_schema(default_conf)
|
||||
|
||||
|
||||
@@ -1075,7 +1074,7 @@ def test_load_config_default_exchange(all_conf) -> None:
|
||||
|
||||
assert "exchange" not in all_conf
|
||||
|
||||
with pytest.raises(ValidationError, match=r"'exchange' is a required property"):
|
||||
with pytest.raises(ConfigurationError, match=r"'exchange' is a required property"):
|
||||
validate_config_schema(all_conf)
|
||||
|
||||
|
||||
@@ -1088,14 +1087,14 @@ def test_load_config_default_exchange_name(all_conf) -> None:
|
||||
|
||||
assert "name" not in all_conf["exchange"]
|
||||
|
||||
with pytest.raises(ValidationError, match=r"'name' is a required property"):
|
||||
with pytest.raises(ConfigurationError, match=r"'name' is a required property"):
|
||||
validate_config_schema(all_conf)
|
||||
|
||||
|
||||
def test_load_config_stoploss_exchange_limit_ratio(all_conf) -> None:
|
||||
all_conf["order_types"]["stoploss_on_exchange_limit_ratio"] = 1.15
|
||||
|
||||
with pytest.raises(ValidationError, match=r"1.15 is greater than the maximum"):
|
||||
with pytest.raises(ConfigurationError, match=r"1.15 is greater than the maximum"):
|
||||
validate_config_schema(all_conf)
|
||||
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import talib.abstract as ta
|
||||
def test_talib_bollingerbands_near_zero_values():
|
||||
inputs = pd.DataFrame(
|
||||
[
|
||||
{"close": 0.00000010},
|
||||
{"close": 0.00000011},
|
||||
{"close": 0.00000012},
|
||||
{"close": 0.00000013},
|
||||
{"close": 0.00000014},
|
||||
{"close": 0.000010},
|
||||
{"close": 0.000011},
|
||||
{"close": 0.000012},
|
||||
{"close": 0.000013},
|
||||
{"close": 0.000014},
|
||||
]
|
||||
)
|
||||
bollinger = ta.BBANDS(inputs, matype=0, timeperiod=2)
|
||||
|
||||
Reference in New Issue
Block a user