Merge branch 'develop' into binance-public-data
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
from datetime import datetime, timedelta
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
@@ -44,7 +45,7 @@ from freqtrade.enums import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.persistence.models import init_db
|
||||
from freqtrade.persistence.pairlock_middleware import PairLocks
|
||||
from freqtrade.util import dt_floor_day, dt_now, dt_utc
|
||||
from freqtrade.util import dt_utc
|
||||
from tests.conftest import (
|
||||
CURRENT_TEST_STRATEGY,
|
||||
EXMS,
|
||||
@@ -571,7 +572,7 @@ def test_create_datadir_failed(caplog):
|
||||
assert log_has("`create-userdir` requires --userdir to be set.", caplog)
|
||||
|
||||
|
||||
def test_create_datadir(caplog, mocker):
|
||||
def test_create_datadir(mocker):
|
||||
cud = mocker.patch(
|
||||
"freqtrade.configuration.directory_operations.create_userdata_dir", MagicMock()
|
||||
)
|
||||
@@ -585,26 +586,46 @@ def test_create_datadir(caplog, mocker):
|
||||
assert csf.call_count == 1
|
||||
|
||||
|
||||
def test_start_new_strategy(mocker, caplog):
|
||||
wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
|
||||
def test_start_new_strategy(caplog, user_dir):
|
||||
strategy_dir = user_dir / "strategies"
|
||||
strategy_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
assert strategy_dir.is_dir()
|
||||
args = ["new-strategy", "--strategy", "CoolNewStrategy"]
|
||||
start_new_strategy(get_args(args))
|
||||
assert strategy_dir.exists()
|
||||
assert (strategy_dir / "CoolNewStrategy.py").exists()
|
||||
|
||||
assert wt_mock.call_count == 1
|
||||
assert "CoolNewStrategy" in wt_mock.call_args_list[0][0][0]
|
||||
assert log_has_re("Writing strategy to .*", caplog)
|
||||
|
||||
mocker.patch("freqtrade.configuration.setup_utils_configuration")
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
with pytest.raises(
|
||||
OperationalException, match=r".* already exists. Please choose another Strategy Name\."
|
||||
):
|
||||
start_new_strategy(get_args(args))
|
||||
|
||||
args = ["new-strategy", "--strategy", "CoolNewStrategy", "--strategy-path", str(user_dir)]
|
||||
start_new_strategy(get_args(args))
|
||||
assert (user_dir / "CoolNewStrategy.py").exists()
|
||||
|
||||
def test_start_new_strategy_no_arg(mocker, caplog):
|
||||
# strategy-path that doesn't exist
|
||||
args = [
|
||||
"new-strategy",
|
||||
"--strategy",
|
||||
"CoolNewStrategy",
|
||||
"--strategy-path",
|
||||
str(user_dir / "nonexistent"),
|
||||
]
|
||||
start_new_strategy(get_args(args))
|
||||
assert (user_dir / "CoolNewStrategy.py").exists()
|
||||
|
||||
assert log_has_re("Creating strategy directory .*", caplog)
|
||||
assert (user_dir / "nonexistent").is_dir()
|
||||
assert (user_dir / "nonexistent" / "CoolNewStrategy.py").exists()
|
||||
|
||||
shutil.rmtree(str(user_dir))
|
||||
|
||||
|
||||
def test_start_new_strategy_no_arg():
|
||||
args = [
|
||||
"new-strategy",
|
||||
]
|
||||
@@ -757,7 +778,13 @@ def test_download_data_keyboardInterrupt(mocker, markets):
|
||||
assert dl_mock.call_count == 1
|
||||
|
||||
|
||||
def test_download_data_timerange(mocker, markets):
|
||||
@pytest.mark.parametrize("time", ["00:00", "00:03", "00:30", "23:56"])
|
||||
@pytest.mark.parametrize(
|
||||
"tzoffset",
|
||||
["00:00", "+01:00", "-01:00", "+05:00", "-05:00"],
|
||||
)
|
||||
def test_download_data_timerange(mocker, markets, time_machine, time, tzoffset):
|
||||
time_machine.move_to(f"2024-11-01 {time}:00 {tzoffset}")
|
||||
dl_mock = mocker.patch(
|
||||
"freqtrade.data.history.history_utils.refresh_backtest_ohlcv_data",
|
||||
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]),
|
||||
@@ -797,8 +824,9 @@ def test_download_data_timerange(mocker, markets):
|
||||
start_download_data(pargs)
|
||||
assert dl_mock.call_count == 1
|
||||
# 20days ago
|
||||
days_ago = dt_floor_day(dt_now() - timedelta(days=20)).timestamp()
|
||||
assert dl_mock.call_args_list[0][1]["timerange"].startts == days_ago
|
||||
days_ago = datetime.now() - timedelta(days=20)
|
||||
days_ago = dt_utc(days_ago.year, days_ago.month, days_ago.day)
|
||||
assert dl_mock.call_args_list[0][1]["timerange"].startts == days_ago.timestamp()
|
||||
|
||||
dl_mock.reset_mock()
|
||||
args = [
|
||||
|
||||
+6
-7
@@ -5,7 +5,6 @@ import re
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from unittest.mock import MagicMock, Mock, PropertyMock
|
||||
|
||||
import numpy as np
|
||||
@@ -124,7 +123,7 @@ def get_args(args):
|
||||
return Arguments(args).get_parsed_arg()
|
||||
|
||||
|
||||
def generate_trades_history(n_rows, start_date: Optional[datetime] = None, days=5):
|
||||
def generate_trades_history(n_rows, start_date: datetime | None = None, days=5):
|
||||
np.random.seed(42)
|
||||
if not start_date:
|
||||
start_date = datetime(2020, 1, 1, tzinfo=timezone.utc)
|
||||
@@ -206,7 +205,7 @@ def generate_test_data_raw(timeframe: str, size: int, start: str = "2020-07-05",
|
||||
"""Generates data in the ohlcv format used by ccxt"""
|
||||
df = generate_test_data(timeframe, size, start, random_seed)
|
||||
df["date"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000
|
||||
return list(list(x) for x in zip(*(df[x].values.tolist() for x in df.columns)))
|
||||
return list(list(x) for x in zip(*(df[x].values.tolist() for x in df.columns), strict=False))
|
||||
|
||||
|
||||
# Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines
|
||||
@@ -363,8 +362,8 @@ def patch_get_signal(
|
||||
exit_long=False,
|
||||
enter_short=False,
|
||||
exit_short=False,
|
||||
enter_tag: Optional[str] = None,
|
||||
exit_tag: Optional[str] = None,
|
||||
enter_tag: str | None = None,
|
||||
exit_tag: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
:param mocker: mocker to patch IStrategy class
|
||||
@@ -395,7 +394,7 @@ def patch_get_signal(
|
||||
freqtrade.exchange.refresh_latest_ohlcv = lambda p: None
|
||||
|
||||
|
||||
def create_mock_trades(fee, is_short: Optional[bool] = False, use_db: bool = True):
|
||||
def create_mock_trades(fee, is_short: bool | None = False, use_db: bool = True):
|
||||
"""
|
||||
Create some fake trades ...
|
||||
:param is_short: Optional bool, None creates a mix of long and short trades.
|
||||
@@ -474,7 +473,7 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True):
|
||||
Trade.session.flush()
|
||||
|
||||
|
||||
def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool = True):
|
||||
def create_mock_trades_usdt(fee, is_short: bool | None = False, use_db: bool = True):
|
||||
"""
|
||||
Create some fake trades ...
|
||||
"""
|
||||
|
||||
@@ -503,7 +503,7 @@ def test_calculate_max_drawdown2():
|
||||
]
|
||||
|
||||
dates = [dt_utc(2020, 1, 1) + timedelta(days=i) for i in range(len(values))]
|
||||
df = DataFrame(zip(values, dates), columns=["profit", "open_date"])
|
||||
df = DataFrame(zip(values, dates, strict=False), columns=["profit", "open_date"])
|
||||
# sort by profit and reset index
|
||||
df = df.sort_values("profit").reset_index(drop=True)
|
||||
df1 = df.copy()
|
||||
@@ -522,11 +522,11 @@ def test_calculate_max_drawdown2():
|
||||
assert drawdown.drawdown_abs == 0.091755
|
||||
assert pytest.approx(drawdown.relative_account_drawdown) == 0.32129575
|
||||
|
||||
df = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"])
|
||||
df = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"])
|
||||
with pytest.raises(ValueError, match="No losing trade, therefore no drawdown."):
|
||||
calculate_max_drawdown(df, date_col="open_date", value_col="profit")
|
||||
|
||||
df1 = DataFrame(zip(values[:5], dates[:5]), columns=["profit", "open_date"])
|
||||
df1 = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"])
|
||||
df1.loc[:, "profit"] = df1["profit"] * -1
|
||||
# No winning trade ...
|
||||
drawdown = calculate_max_drawdown(df1, date_col="open_date", value_col="profit")
|
||||
@@ -548,7 +548,7 @@ def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, r
|
||||
"""
|
||||
init_date = datetime(2020, 1, 1, tzinfo=timezone.utc)
|
||||
dates = [init_date + timedelta(days=i) for i in range(len(profits))]
|
||||
df = DataFrame(zip(profits, dates), columns=["profit_abs", "open_date"])
|
||||
df = DataFrame(zip(profits, dates, strict=False), columns=["profit_abs", "open_date"])
|
||||
# sort by profit and reset index
|
||||
df = df.sort_values("profit_abs").reset_index(drop=True)
|
||||
df1 = df.copy()
|
||||
|
||||
@@ -1069,7 +1069,6 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag
|
||||
assert order["type"] == "limit"
|
||||
assert order["symbol"] == "ETH/BTC"
|
||||
assert order["amount"] == 1
|
||||
assert order["leverage"] == leverage
|
||||
assert order["cost"] == 1 * 200
|
||||
|
||||
|
||||
|
||||
@@ -3483,16 +3483,17 @@ def test_locked_pairs(
|
||||
exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS),
|
||||
)
|
||||
trade.close(ticker_usdt_sell_down()["bid"])
|
||||
assert freqtrade.strategy.is_pair_locked(trade.pair, side="*")
|
||||
assert not freqtrade.strategy.is_pair_locked(trade.pair, side="*")
|
||||
# Both sides are locked
|
||||
assert freqtrade.strategy.is_pair_locked(trade.pair, side="long")
|
||||
assert freqtrade.strategy.is_pair_locked(trade.pair, side="short")
|
||||
assert freqtrade.strategy.is_pair_locked(trade.pair, side="long") != is_short
|
||||
assert freqtrade.strategy.is_pair_locked(trade.pair, side="short") == is_short
|
||||
|
||||
# reinit - should buy other pair.
|
||||
caplog.clear()
|
||||
freqtrade.enter_positions()
|
||||
direction = "short" if is_short else "long"
|
||||
|
||||
assert log_has_re(rf"Pair {trade.pair} \* is locked.*", caplog)
|
||||
assert log_has_re(rf"Pair {trade.pair} {direction} is locked.*", caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import timedelta
|
||||
from typing import NamedTuple, Optional
|
||||
from typing import NamedTuple
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
@@ -20,7 +20,7 @@ class BTrade(NamedTuple):
|
||||
exit_reason: ExitType
|
||||
open_tick: int
|
||||
close_tick: int
|
||||
enter_tag: Optional[str] = None
|
||||
enter_tag: str | None = None
|
||||
is_short: bool = False
|
||||
|
||||
|
||||
@@ -36,15 +36,15 @@ class BTContainer(NamedTuple):
|
||||
profit_perc: float
|
||||
trailing_stop: bool = False
|
||||
trailing_only_offset_is_reached: bool = False
|
||||
trailing_stop_positive: Optional[float] = None
|
||||
trailing_stop_positive: float | None = None
|
||||
trailing_stop_positive_offset: float = 0.0
|
||||
use_exit_signal: bool = False
|
||||
use_custom_stoploss: bool = False
|
||||
custom_entry_price: Optional[float] = None
|
||||
custom_exit_price: Optional[float] = None
|
||||
custom_entry_price: float | None = None
|
||||
custom_exit_price: float | None = None
|
||||
leverage: float = 1.0
|
||||
timeout: Optional[int] = None
|
||||
adjust_entry_price: Optional[float] = None
|
||||
timeout: int | None = None
|
||||
adjust_entry_price: float | None = None
|
||||
|
||||
|
||||
def _get_frame_time_from_offset(offset):
|
||||
|
||||
@@ -1121,6 +1121,70 @@ tc53 = BTContainer(
|
||||
trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=True)],
|
||||
)
|
||||
|
||||
# Test 54: Switch position from long to short
|
||||
tc54 = BTContainer(
|
||||
data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 0, 0],
|
||||
[1, 5000, 5000, 4951, 5000, 6172, 0, 0, 0, 0],
|
||||
[2, 4910, 5150, 4910, 5100, 6172, 0, 0, 1, 0], # Enter short signal being ignored
|
||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 1, 1, 0], # exit - re-enter short
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 1],
|
||||
[5, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
],
|
||||
stop_loss=-0.10,
|
||||
roi={"0": 0.10},
|
||||
profit_perc=0.00,
|
||||
use_exit_signal=True,
|
||||
trades=[
|
||||
BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=False),
|
||||
BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=4, close_tick=5, is_short=True),
|
||||
],
|
||||
)
|
||||
|
||||
# Test 55: Switch position from short to long
|
||||
tc55 = BTContainer(
|
||||
data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5000, 4951, 5000, 6172, 1, 0, 0, 0], # Enter long signal being ignored
|
||||
[2, 4910, 5150, 4910, 5100, 6172, 1, 0, 0, 1], # Exit - reenter long
|
||||
[3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 1, 0, 0],
|
||||
[5, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
],
|
||||
stop_loss=-0.10,
|
||||
roi={"0": 0.10},
|
||||
profit_perc=-0.04,
|
||||
use_exit_signal=True,
|
||||
trades=[
|
||||
BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=3, is_short=True),
|
||||
BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=3, close_tick=5, is_short=False),
|
||||
],
|
||||
)
|
||||
|
||||
# Test 56: Switch position from long to short
|
||||
tc56 = BTContainer(
|
||||
data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 0, 0],
|
||||
[1, 5000, 5000, 4951, 5000, 6172, 0, 0, 0, 0],
|
||||
[2, 4910, 5150, 4910, 5100, 6172, 0, 0, 1, 0], # exit on stoploss - re-enter short
|
||||
[3, 5100, 5100, 4888, 4950, 6172, 0, 0, 0, 0],
|
||||
[4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 1],
|
||||
[5, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0],
|
||||
],
|
||||
stop_loss=-0.02,
|
||||
roi={"0": 0.10},
|
||||
profit_perc=-0.0,
|
||||
use_exit_signal=True,
|
||||
trades=[
|
||||
BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3, is_short=False),
|
||||
BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=3, close_tick=5, is_short=True),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
TESTS = [
|
||||
tc0,
|
||||
tc1,
|
||||
@@ -1176,6 +1240,9 @@ TESTS = [
|
||||
tc51,
|
||||
tc52,
|
||||
tc53,
|
||||
tc54,
|
||||
tc55,
|
||||
tc56,
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -189,7 +189,6 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
|
||||
"--timeframe",
|
||||
"1m",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
"--timerange",
|
||||
":100",
|
||||
"--export-filename",
|
||||
@@ -214,10 +213,6 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
|
||||
assert "position_stacking" in config
|
||||
assert log_has("Parameter --enable-position-stacking detected ...", caplog)
|
||||
|
||||
assert "use_max_market_positions" in config
|
||||
assert log_has("Parameter --disable-max-market-positions detected ...", caplog)
|
||||
assert log_has("max_open_trades set to unlimited ...", caplog)
|
||||
|
||||
assert "timerange" in config
|
||||
assert log_has("Parameter --timerange detected: {} ...".format(config["timerange"]), caplog)
|
||||
|
||||
@@ -1681,6 +1676,131 @@ def test_backtest_multi_pair_detail(
|
||||
assert len(evaluate_result_multi(results["results"], "5m", 1)) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("use_detail", [True, False])
|
||||
@pytest.mark.parametrize("pair", ["ADA/USDT", "LTC/USDT"])
|
||||
@pytest.mark.parametrize("tres", [0, 20, 30])
|
||||
def test_backtest_multi_pair_detail_simplified(
|
||||
default_conf_usdt,
|
||||
fee,
|
||||
mocker,
|
||||
tres,
|
||||
pair,
|
||||
use_detail,
|
||||
):
|
||||
"""
|
||||
literally the same as test_backtest_multi_pair_detail
|
||||
but with an "always enter" strategy, exiting after about half of the candle duration.
|
||||
"""
|
||||
|
||||
def _always_buy(dataframe, metadata):
|
||||
"""
|
||||
Buy every xth candle - sell every other xth -2 (hold on to pairs a bit)
|
||||
"""
|
||||
dataframe["enter_long"] = 1
|
||||
dataframe["enter_short"] = 0
|
||||
dataframe["exit_short"] = 0
|
||||
return dataframe
|
||||
|
||||
def custom_exit(
|
||||
trade: Trade,
|
||||
current_time: datetime,
|
||||
**kwargs,
|
||||
) -> str | bool | None:
|
||||
# Exit within the same candle.
|
||||
if (trade.open_date_utc + timedelta(minutes=20)) < current_time:
|
||||
return "exit after 20 minutes"
|
||||
|
||||
default_conf_usdt.update(
|
||||
{
|
||||
"runmode": "backtest",
|
||||
"stoploss": -1.0,
|
||||
"minimal_roi": {"0": 100},
|
||||
}
|
||||
)
|
||||
|
||||
if use_detail:
|
||||
default_conf_usdt["timeframe_detail"] = "5m"
|
||||
|
||||
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"))
|
||||
mocker.patch(f"{EXMS}.get_fee", fee)
|
||||
patch_exchange(mocker)
|
||||
|
||||
raw_candles_5m = generate_test_data("5m", 1000, "2022-01-03 12:00:00+00:00")
|
||||
raw_candles = ohlcv_fill_up_missing_data(raw_candles_5m, "1h", "dummy")
|
||||
|
||||
pairs = ["ADA/USDT", "DASH/USDT", "ETH/USDT", "LTC/USDT", "NXT/USDT"]
|
||||
data = {pair: raw_candles for pair in pairs}
|
||||
detail_data = {pair: raw_candles_5m for pair in pairs}
|
||||
|
||||
# Only use 500 lines to increase performance
|
||||
data = trim_dictlist(data, -200)
|
||||
|
||||
# Remove data for one pair from the beginning of the data
|
||||
if tres > 0:
|
||||
data[pair] = data[pair][tres:].reset_index()
|
||||
default_conf_usdt["timeframe"] = "1h"
|
||||
default_conf_usdt["max_open_trades"] = 3
|
||||
|
||||
backtesting = Backtesting(default_conf_usdt)
|
||||
vr_spy = mocker.spy(backtesting, "validate_row")
|
||||
bl_spy = mocker.spy(backtesting, "backtest_loop")
|
||||
backtesting.detail_data = detail_data
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
backtesting.strategy.bot_loop_start = MagicMock()
|
||||
backtesting.strategy.advise_entry = _always_buy # Override
|
||||
backtesting.strategy.advise_exit = _always_buy # Override
|
||||
backtesting.strategy.custom_exit = custom_exit # Override
|
||||
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
|
||||
backtest_conf = {
|
||||
"processed": deepcopy(processed),
|
||||
"start_date": min_date,
|
||||
"end_date": max_date,
|
||||
}
|
||||
|
||||
results = backtesting.backtest(**backtest_conf)
|
||||
|
||||
# bot_loop_start is called once per candle.
|
||||
# assert backtesting.strategy.bot_loop_start.call_count == 83
|
||||
# Validated row once per candle and pair
|
||||
assert vr_spy.call_count == 415
|
||||
|
||||
if use_detail:
|
||||
# Backtest loop is called once per candle per pair
|
||||
# Exact numbers depend on trade state - but should be around 3_800
|
||||
assert bl_spy.call_count > 3_350
|
||||
assert bl_spy.call_count < 3_800
|
||||
else:
|
||||
assert bl_spy.call_count < 995
|
||||
|
||||
# Make sure we have parallel trades
|
||||
assert len(evaluate_result_multi(results["results"], "1h", 2)) > 0
|
||||
# make sure we don't have trades with more than configured max_open_trades
|
||||
assert len(evaluate_result_multi(results["results"], "1h", 3)) == 0
|
||||
|
||||
# # Cached data correctly removed amounts
|
||||
offset = 1 if tres == 0 else 0
|
||||
removed_candles = len(data[pair]) - offset
|
||||
assert len(backtesting.dataprovider.get_analyzed_dataframe(pair, "1h")[0]) == removed_candles
|
||||
assert (
|
||||
len(backtesting.dataprovider.get_analyzed_dataframe("NXT/USDT", "1h")[0])
|
||||
== len(data["NXT/USDT"]) - 1
|
||||
)
|
||||
|
||||
backtesting.strategy.max_open_trades = 1
|
||||
backtesting.config.update({"max_open_trades": 1})
|
||||
backtest_conf = {
|
||||
"processed": deepcopy(processed),
|
||||
"start_date": min_date,
|
||||
"end_date": max_date,
|
||||
}
|
||||
results = backtesting.backtest(**backtest_conf)
|
||||
assert len(evaluate_result_multi(results["results"], "1h", 1)) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("use_detail", [True, False])
|
||||
def test_backtest_multi_pair_long_short_switch(
|
||||
default_conf_usdt,
|
||||
@@ -1774,7 +1894,7 @@ def test_backtest_multi_pair_long_short_switch(
|
||||
|
||||
if use_detail:
|
||||
# Backtest loop is called once per candle per pair
|
||||
assert bl_spy.call_count == 1071
|
||||
assert bl_spy.call_count == 1523
|
||||
else:
|
||||
assert bl_spy.call_count == 479
|
||||
|
||||
@@ -1784,7 +1904,7 @@ def test_backtest_multi_pair_long_short_switch(
|
||||
assert len(evaluate_result_multi(results["results"], "5m", 1)) == 0
|
||||
|
||||
# Expect 26 results initially
|
||||
assert len(results["results"]) == 30
|
||||
assert len(results["results"]) == 53
|
||||
|
||||
|
||||
def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
|
||||
@@ -1811,14 +1931,12 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
|
||||
"--timerange",
|
||||
"1510694220-1510700340",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
]
|
||||
args = get_args(args)
|
||||
start_backtesting(args)
|
||||
# check the logs, that will contain the backtest result
|
||||
exists = [
|
||||
"Parameter -i/--timeframe detected ... Using timeframe: 1m ...",
|
||||
"Ignoring max_open_trades (--disable-max-market-positions was used) ...",
|
||||
"Parameter --timerange detected: 1510694220-1510700340 ...",
|
||||
f"Using data directory: {testdatadir} ...",
|
||||
"Loading data from 2017-11-14 20:57:00 up to 2017-11-14 22:59:00 (0 days).",
|
||||
@@ -1892,7 +2010,6 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
"--timerange",
|
||||
"1510694220-1510700340",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
"--strategy-list",
|
||||
CURRENT_TEST_STRATEGY,
|
||||
"StrategyTestV2",
|
||||
@@ -1909,7 +2026,6 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
# check the logs, that will contain the backtest result
|
||||
exists = [
|
||||
"Parameter -i/--timeframe detected ... Using timeframe: 1m ...",
|
||||
"Ignoring max_open_trades (--disable-max-market-positions was used) ...",
|
||||
"Parameter --timerange detected: 1510694220-1510700340 ...",
|
||||
f"Using data directory: {testdatadir} ...",
|
||||
"Loading data from 2017-11-14 20:57:00 up to 2017-11-14 22:59:00 (0 days).",
|
||||
@@ -2030,7 +2146,6 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
"--timerange",
|
||||
"1510694220-1510700340",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
"--breakdown",
|
||||
"day",
|
||||
"--strategy-list",
|
||||
@@ -2043,7 +2158,6 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
# check the logs, that will contain the backtest result
|
||||
exists = [
|
||||
"Parameter -i/--timeframe detected ... Using timeframe: 1m ...",
|
||||
"Ignoring max_open_trades (--disable-max-market-positions was used) ...",
|
||||
"Parameter --timerange detected: 1510694220-1510700340 ...",
|
||||
f"Using data directory: {testdatadir} ...",
|
||||
"Loading data from 2017-11-14 20:57:00 up to 2017-11-14 22:59:00 (0 days).",
|
||||
@@ -2468,7 +2582,6 @@ def test_backtest_start_multi_strat_caching(
|
||||
"--timerange",
|
||||
"1510694220-1510700340",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
"--cache",
|
||||
cache,
|
||||
"--strategy-list",
|
||||
@@ -2495,7 +2608,6 @@ def test_backtest_start_multi_strat_caching(
|
||||
exists = [
|
||||
"Running backtesting for Strategy StrategyTestV2",
|
||||
"Running backtesting for Strategy StrategyTestV3",
|
||||
"Ignoring max_open_trades (--disable-max-market-positions was used) ...",
|
||||
"Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:59:00 (0 days).",
|
||||
]
|
||||
elif run_id == "2" and min_backtest_date < start_time:
|
||||
@@ -2508,7 +2620,6 @@ def test_backtest_start_multi_strat_caching(
|
||||
exists = [
|
||||
"Reusing result of previous backtest for StrategyTestV2",
|
||||
"Running backtesting for Strategy StrategyTestV3",
|
||||
"Ignoring max_open_trades (--disable-max-market-positions was used) ...",
|
||||
"Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:59:00 (0 days).",
|
||||
]
|
||||
assert backtestmock.call_count == 1
|
||||
|
||||
+186
-150
@@ -14,7 +14,7 @@ from freqtrade.data.history import load_data
|
||||
from freqtrade.enums import ExitType, RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.hyperopt import Hyperopt
|
||||
from freqtrade.optimize.hyperopt_auto import HyperOptAuto
|
||||
from freqtrade.optimize.hyperopt.hyperopt_auto import HyperOptAuto
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
||||
from freqtrade.optimize.space import SKDecimal
|
||||
@@ -102,7 +102,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
|
||||
"--timerange",
|
||||
":100",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
"--epochs",
|
||||
"1000",
|
||||
"--spaces",
|
||||
@@ -126,10 +125,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
|
||||
assert "position_stacking" in config
|
||||
assert log_has("Parameter --enable-position-stacking detected ...", caplog)
|
||||
|
||||
assert "use_max_market_positions" in config
|
||||
assert log_has("Parameter --disable-max-market-positions detected ...", caplog)
|
||||
assert log_has("max_open_trades set to unlimited ...", caplog)
|
||||
|
||||
assert "timerange" in config
|
||||
assert log_has("Parameter --timerange detected: {} ...".format(config["timerange"]), caplog)
|
||||
|
||||
@@ -227,7 +222,7 @@ def test_start_no_data(mocker, hyperopt_conf, tmp_path) -> None:
|
||||
patched_configuration_load_config_file(mocker, hyperopt_conf)
|
||||
mocker.patch("freqtrade.data.history.load_pair_history", MagicMock(return_value=pd.DataFrame))
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -320,12 +315,17 @@ def test_roi_table_generation(hyperopt) -> None:
|
||||
"roi_p3": 3,
|
||||
}
|
||||
|
||||
assert hyperopt.custom_hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0}
|
||||
assert hyperopt.hyperopter.custom_hyperopt.generate_roi_table(params) == {
|
||||
0: 6,
|
||||
15: 3,
|
||||
25: 1,
|
||||
30: 0,
|
||||
}
|
||||
|
||||
|
||||
def test_params_no_optimize_details(hyperopt) -> None:
|
||||
hyperopt.config["spaces"] = ["buy"]
|
||||
res = hyperopt._get_no_optimize_details()
|
||||
hyperopt.hyperopter.config["spaces"] = ["buy"]
|
||||
res = hyperopt.hyperopter._get_no_optimize_details()
|
||||
assert isinstance(res, dict)
|
||||
assert "trailing" in res
|
||||
assert res["trailing"]["trailing_stop"] is False
|
||||
@@ -338,21 +338,23 @@ def test_params_no_optimize_details(hyperopt) -> None:
|
||||
|
||||
|
||||
def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
# Dummy-reduce points to ensure scikit-learn is forced to generate new values
|
||||
mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.INITIAL_POINTS", 2)
|
||||
|
||||
parallel = mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel",
|
||||
@@ -372,8 +374,8 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
||||
del hyperopt_conf["timeframe"]
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -384,10 +386,12 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
||||
# Should be called for historical candle data
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_entry")
|
||||
assert (
|
||||
hyperopt.hyperopter.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
)
|
||||
assert hasattr(hyperopt.hyperopter.backtesting, "_position_stacking")
|
||||
|
||||
|
||||
def test_hyperopt_format_results(hyperopt):
|
||||
@@ -466,7 +470,7 @@ def test_hyperopt_format_results(hyperopt):
|
||||
|
||||
def test_populate_indicators(hyperopt, testdatadir) -> None:
|
||||
data = load_data(testdatadir, "1m", ["UNITTEST/BTC"], fill_up_missing=True)
|
||||
dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data)
|
||||
dataframes = hyperopt.hyperopter.backtesting.strategy.advise_all_indicators(data)
|
||||
dataframe = dataframes["UNITTEST/BTC"]
|
||||
|
||||
# Check if some indicators are generated. We will not test all of them
|
||||
@@ -526,15 +530,20 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
|
||||
"final_balance": 1000,
|
||||
}
|
||||
|
||||
mocker.patch("freqtrade.optimize.hyperopt.Backtesting.backtest", return_value=backtest_result)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.Backtesting.backtest",
|
||||
return_value=backtest_result,
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
return_value=(dt_utc(2017, 12, 10), dt_utc(2017, 12, 13)),
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.object(Path, "open")
|
||||
mocker.patch("freqtrade.configuration.config_validation.validate_config_schema")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.load", return_value={"XRP/BTC": None})
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.load", return_value={"XRP/BTC": None}
|
||||
)
|
||||
|
||||
optimizer_param = {
|
||||
"buy_plusdi": 0.02,
|
||||
@@ -594,10 +603,12 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
|
||||
}
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.min_date = dt_utc(2017, 12, 10)
|
||||
hyperopt.max_date = dt_utc(2017, 12, 13)
|
||||
hyperopt.init_spaces()
|
||||
generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values()))
|
||||
hyperopt.hyperopter.min_date = dt_utc(2017, 12, 10)
|
||||
hyperopt.hyperopter.max_date = dt_utc(2017, 12, 13)
|
||||
hyperopt.hyperopter.init_spaces()
|
||||
generate_optimizer_value = hyperopt.hyperopter.generate_optimizer(
|
||||
list(optimizer_param.values())
|
||||
)
|
||||
assert generate_optimizer_value == response_expected
|
||||
|
||||
|
||||
@@ -608,8 +619,8 @@ def test_clean_hyperopt(mocker, hyperopt_conf, caplog):
|
||||
"freqtrade.strategy.hyper.HyperStrategyMixin.load_params_from_file",
|
||||
MagicMock(return_value={}),
|
||||
)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.Path.is_file", MagicMock(return_value=True))
|
||||
unlinkmock = mocker.patch("freqtrade.optimize.hyperopt.Path.unlink", MagicMock())
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.Path.is_file", MagicMock(return_value=True))
|
||||
unlinkmock = mocker.patch("freqtrade.optimize.hyperopt.hyperopt.Path.unlink", MagicMock())
|
||||
h = Hyperopt(hyperopt_conf)
|
||||
|
||||
assert unlinkmock.call_count == 2
|
||||
@@ -617,17 +628,19 @@ def test_clean_hyperopt(mocker, hyperopt_conf, caplog):
|
||||
|
||||
|
||||
def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -663,8 +676,8 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
||||
)
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -682,16 +695,18 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
||||
|
||||
|
||||
def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -719,8 +734,8 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
||||
hyperopt_conf.update({"print_json": True})
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -737,16 +752,18 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
||||
|
||||
|
||||
def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -775,8 +792,8 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||
)
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -790,16 +807,18 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||
|
||||
|
||||
def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -821,8 +840,8 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
|
||||
hyperopt_conf.update({"spaces": "roi stoploss"})
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -833,21 +852,23 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_entry")
|
||||
assert (
|
||||
hyperopt.hyperopter.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
)
|
||||
assert hasattr(hyperopt.hyperopter.backtesting, "_position_stacking")
|
||||
|
||||
|
||||
def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None:
|
||||
mocker.patch("freqtrade.optimize.hyperopt.dump", MagicMock())
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump", MagicMock())
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -860,34 +881,37 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None:
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space", return_value=[]
|
||||
"freqtrade.optimize.hyperopt.hyperopt_auto.HyperOptAuto._generate_indicator_space",
|
||||
return_value=[],
|
||||
)
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"):
|
||||
hyperopt.init_spaces()
|
||||
hyperopt.hyperopter.init_spaces()
|
||||
|
||||
hyperopt.config["hyperopt_ignore_missing_space"] = True
|
||||
caplog.clear()
|
||||
hyperopt.init_spaces()
|
||||
hyperopt.hyperopter.init_spaces()
|
||||
assert log_has_re(r"The 'protection' space is included into *", caplog)
|
||||
assert hyperopt.protection_space == []
|
||||
assert hyperopt.hyperopter.protection_space == []
|
||||
|
||||
|
||||
def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -909,8 +933,8 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
||||
hyperopt_conf.update({"spaces": "buy"})
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -921,23 +945,27 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
||||
assert dumper.called
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_entry")
|
||||
assert (
|
||||
hyperopt.hyperopter.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
)
|
||||
assert hasattr(hyperopt.hyperopter.backtesting, "_position_stacking")
|
||||
|
||||
|
||||
def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.dump")
|
||||
dumper = mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump")
|
||||
dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.calculate_market_change", return_value=1.5
|
||||
)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
|
||||
@@ -963,8 +991,8 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||
)
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -975,10 +1003,12 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||
assert dumper.called
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_exit")
|
||||
assert hasattr(hyperopt.hyperopter.backtesting.strategy, "advise_entry")
|
||||
assert (
|
||||
hyperopt.hyperopter.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
|
||||
)
|
||||
assert hasattr(hyperopt.hyperopter.backtesting, "_position_stacking")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -990,18 +1020,19 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||
],
|
||||
)
|
||||
def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None:
|
||||
mocker.patch("freqtrade.optimize.hyperopt.dump", MagicMock())
|
||||
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt_optimizer.dump", MagicMock())
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.file_dump_json")
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.backtesting.Backtesting.load_bt_data",
|
||||
MagicMock(return_value=(MagicMock(), None)),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.get_timerange",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.get_timerange",
|
||||
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
|
||||
)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space", return_value=[]
|
||||
"freqtrade.optimize.hyperopt.hyperopt_auto.HyperOptAuto._generate_indicator_space",
|
||||
return_value=[],
|
||||
)
|
||||
|
||||
patch_exchange(mocker)
|
||||
@@ -1009,8 +1040,8 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None:
|
||||
hyperopt_conf.update({"spaces": space})
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
hyperopt.hyperopter.backtesting.strategy.advise_all_indicators = MagicMock()
|
||||
hyperopt.hyperopter.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
|
||||
|
||||
with pytest.raises(OperationalException, match=f"The '{space}' space is included into *"):
|
||||
hyperopt.start()
|
||||
@@ -1020,7 +1051,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f"{EXMS}.get_fee", fee)
|
||||
# Dummy-reduce points to ensure scikit-learn is forced to generate new values
|
||||
mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.INITIAL_POINTS", 2)
|
||||
(tmp_path / "hyperopt_results").mkdir(parents=True)
|
||||
# No hyperopt needed
|
||||
hyperopt_conf.update(
|
||||
@@ -1032,32 +1063,33 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None
|
||||
}
|
||||
)
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
assert hyperopt.backtesting.strategy.bot_started is True
|
||||
assert hyperopt.backtesting.strategy.bot_loop_started is False
|
||||
opt = hyperopt.hyperopter
|
||||
opt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||
assert isinstance(opt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(opt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
assert opt.backtesting.strategy.bot_started is True
|
||||
assert opt.backtesting.strategy.bot_loop_started is False
|
||||
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
|
||||
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
|
||||
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == 1
|
||||
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
|
||||
assert opt.backtesting.strategy.buy_rsi.in_space is True
|
||||
assert opt.backtesting.strategy.buy_rsi.value == 35
|
||||
assert opt.backtesting.strategy.sell_rsi.value == 74
|
||||
assert opt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||
assert opt.backtesting.strategy.max_open_trades == 1
|
||||
buy_rsi_range = opt.backtesting.strategy.buy_rsi.range
|
||||
assert isinstance(buy_rsi_range, range)
|
||||
# Range from 0 - 50 (inclusive)
|
||||
assert len(list(buy_rsi_range)) == 51
|
||||
|
||||
hyperopt.start()
|
||||
# All values should've changed.
|
||||
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value != 30
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.value != 35
|
||||
assert hyperopt.backtesting.strategy.sell_rsi.value != 74
|
||||
assert hyperopt.backtesting.strategy.max_open_trades != 1
|
||||
assert opt.backtesting.strategy.protection_cooldown_lookback.value != 30
|
||||
assert opt.backtesting.strategy.buy_rsi.value != 35
|
||||
assert opt.backtesting.strategy.sell_rsi.value != 74
|
||||
assert opt.backtesting.strategy.max_open_trades != 1
|
||||
|
||||
hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: "ET1"
|
||||
opt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: "ET1"
|
||||
with pytest.raises(OperationalException, match="Estimator ET1 not supported."):
|
||||
hyperopt.get_optimizer([], 2)
|
||||
opt.get_optimizer(2, 42, 2, 2)
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
|
||||
@@ -1068,7 +1100,7 @@ def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path
|
||||
mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=get_markets()))
|
||||
(tmp_path / "hyperopt_results").mkdir(parents=True)
|
||||
# Dummy-reduce points to ensure scikit-learn is forced to generate new values
|
||||
mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2)
|
||||
mocker.patch("freqtrade.optimize.hyperopt.hyperopt.INITIAL_POINTS", 2)
|
||||
# No hyperopt needed
|
||||
hyperopt_conf.update(
|
||||
{
|
||||
@@ -1083,21 +1115,22 @@ def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path
|
||||
}
|
||||
)
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.exchange.get_max_leverage = lambda *x, **xx: 1.0
|
||||
hyperopt.backtesting.exchange.get_min_pair_stake_amount = lambda *x, **xx: 0.00001
|
||||
hyperopt.backtesting.exchange.get_max_pair_stake_amount = lambda *x, **xx: 100.0
|
||||
hyperopt.backtesting.exchange._markets = get_markets()
|
||||
opt = hyperopt.hyperopter
|
||||
opt.backtesting.exchange.get_max_leverage = lambda *x, **xx: 1.0
|
||||
opt.backtesting.exchange.get_min_pair_stake_amount = lambda *x, **xx: 0.00001
|
||||
opt.backtesting.exchange.get_max_pair_stake_amount = lambda *x, **xx: 100.0
|
||||
opt.backtesting.exchange._markets = get_markets()
|
||||
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
assert hyperopt.backtesting.strategy.bot_started is True
|
||||
assert hyperopt.backtesting.strategy.bot_loop_started is False
|
||||
assert isinstance(opt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(opt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
assert opt.backtesting.strategy.bot_started is True
|
||||
assert opt.backtesting.strategy.bot_loop_started is False
|
||||
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
|
||||
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
|
||||
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
|
||||
assert opt.backtesting.strategy.buy_rsi.in_space is True
|
||||
assert opt.backtesting.strategy.buy_rsi.value == 35
|
||||
assert opt.backtesting.strategy.sell_rsi.value == 74
|
||||
assert opt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||
buy_rsi_range = opt.backtesting.strategy.buy_rsi.range
|
||||
assert isinstance(buy_rsi_range, range)
|
||||
# Range from 0 - 50 (inclusive)
|
||||
assert len(list(buy_rsi_range)) == 51
|
||||
@@ -1121,7 +1154,7 @@ def test_in_strategy_auto_hyperopt_per_epoch(mocker, hyperopt_conf, tmp_path, fe
|
||||
}
|
||||
)
|
||||
go = mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.Hyperopt.generate_optimizer",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.HyperOptimizer.generate_optimizer",
|
||||
return_value={
|
||||
"loss": 0.05,
|
||||
"results_explanation": "foo result",
|
||||
@@ -1130,17 +1163,18 @@ def test_in_strategy_auto_hyperopt_per_epoch(mocker, hyperopt_conf, tmp_path, fe
|
||||
},
|
||||
)
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
assert hyperopt.backtesting.strategy.bot_loop_started is False
|
||||
assert hyperopt.backtesting.strategy.bot_started is True
|
||||
opt = hyperopt.hyperopter
|
||||
opt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||
assert isinstance(opt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(opt.backtesting.strategy.buy_rsi, IntParameter)
|
||||
assert opt.backtesting.strategy.bot_loop_started is False
|
||||
assert opt.backtesting.strategy.bot_started is True
|
||||
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
|
||||
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
|
||||
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
|
||||
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
|
||||
assert opt.backtesting.strategy.buy_rsi.in_space is True
|
||||
assert opt.backtesting.strategy.buy_rsi.value == 35
|
||||
assert opt.backtesting.strategy.sell_rsi.value == 74
|
||||
assert opt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||
buy_rsi_range = opt.backtesting.strategy.buy_rsi.range
|
||||
assert isinstance(buy_rsi_range, range)
|
||||
# Range from 0 - 50 (inclusive)
|
||||
assert len(list(buy_rsi_range)) == 51
|
||||
@@ -1184,17 +1218,17 @@ def test_stake_amount_unlimited_max_open_trades(mocker, hyperopt_conf, tmp_path,
|
||||
)
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.Hyperopt._get_params_dict",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.HyperOptimizer._get_params_dict",
|
||||
return_value={"max_open_trades": -1},
|
||||
)
|
||||
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.hyperopter.custom_hyperopt, HyperOptAuto)
|
||||
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == 1
|
||||
assert hyperopt.hyperopter.backtesting.strategy.max_open_trades == 1
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == 1
|
||||
assert hyperopt.hyperopter.backtesting.strategy.max_open_trades == 1
|
||||
|
||||
|
||||
def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> None:
|
||||
@@ -1213,11 +1247,11 @@ def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> N
|
||||
)
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.Hyperopt._get_params_dict",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.HyperOptimizer._get_params_dict",
|
||||
return_value={"max_open_trades": -1},
|
||||
)
|
||||
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.hyperopter.custom_hyperopt, HyperOptAuto)
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -1232,11 +1266,11 @@ def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> N
|
||||
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt.Hyperopt._get_params_dict",
|
||||
"freqtrade.optimize.hyperopt.hyperopt_optimizer.HyperOptimizer._get_params_dict",
|
||||
return_value={"max_open_trades": -1},
|
||||
)
|
||||
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.hyperopter.custom_hyperopt, HyperOptAuto)
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
@@ -1267,9 +1301,9 @@ def test_max_open_trades_consistency(mocker, hyperopt_conf, tmp_path, fee) -> No
|
||||
)
|
||||
hyperopt = Hyperopt(hyperopt_conf)
|
||||
|
||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||
assert isinstance(hyperopt.hyperopter.custom_hyperopt, HyperOptAuto)
|
||||
|
||||
hyperopt.custom_hyperopt.max_open_trades_space = lambda: [
|
||||
hyperopt.hyperopter.custom_hyperopt.max_open_trades_space = lambda: [
|
||||
Integer(1, 10, name="max_open_trades")
|
||||
]
|
||||
|
||||
@@ -1287,11 +1321,13 @@ def test_max_open_trades_consistency(mocker, hyperopt_conf, tmp_path, fee) -> No
|
||||
|
||||
return wrapper
|
||||
|
||||
hyperopt.backtesting.wallets._calculate_unlimited_stake_amount = stake_amount_interceptor(
|
||||
hyperopt.backtesting.wallets._calculate_unlimited_stake_amount
|
||||
hyperopt.hyperopter.backtesting.wallets._calculate_unlimited_stake_amount = (
|
||||
stake_amount_interceptor(
|
||||
hyperopt.hyperopter.backtesting.wallets._calculate_unlimited_stake_amount
|
||||
)
|
||||
)
|
||||
|
||||
hyperopt.start()
|
||||
|
||||
assert hyperopt.backtesting.strategy.max_open_trades == 8
|
||||
assert hyperopt.hyperopter.backtesting.strategy.max_open_trades == 8
|
||||
assert hyperopt.config["max_open_trades"] == 8
|
||||
|
||||
@@ -2144,6 +2144,7 @@ def test_Trade_object_idem():
|
||||
"bt_trades_open",
|
||||
"bt_trades_open_pp",
|
||||
"bt_open_open_trade_count",
|
||||
"bt_open_open_trade_count_candle",
|
||||
"bt_total_profit",
|
||||
"from_json",
|
||||
)
|
||||
@@ -2682,6 +2683,36 @@ def test_select_filled_orders(fee):
|
||||
assert len(orders) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_select_filled_orders_usdt(fee):
|
||||
create_mock_trades_usdt(fee)
|
||||
|
||||
trades = Trade.get_trades().all()
|
||||
|
||||
# Closed buy order, no sell order
|
||||
orders = trades[0].select_filled_orders("buy")
|
||||
assert isinstance(orders, list)
|
||||
assert len(orders) == 1
|
||||
assert orders[0].amount == 2.0
|
||||
assert orders[0].filled == 2.0
|
||||
assert orders[0].side == "buy"
|
||||
assert orders[0].price == 10.0
|
||||
assert orders[0].stake_amount == 20
|
||||
assert orders[0].stake_amount_filled == 20
|
||||
|
||||
orders = trades[3].select_filled_orders("buy")
|
||||
assert isinstance(orders, list)
|
||||
assert len(orders) == 0
|
||||
orders = trades[3].select_filled_or_open_orders()
|
||||
assert isinstance(orders, list)
|
||||
assert len(orders) == 1
|
||||
assert orders[0].price == 2.0
|
||||
assert orders[0].amount == 10
|
||||
assert orders[0].filled == 0
|
||||
assert orders[0].stake_amount == 20
|
||||
assert orders[0].stake_amount_filled == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_order_to_ccxt(limit_buy_order_open, limit_sell_order_usdt_open):
|
||||
order = Order.parse_from_ccxt_object(limit_buy_order_open, "mocked", "buy")
|
||||
|
||||
@@ -2450,7 +2450,7 @@ def test_MarketCapPairList_filter_special_no_pair_from_coingecko(
|
||||
assert pm.whitelist == []
|
||||
|
||||
|
||||
def test_MarketCapPairList_exceptions(mocker, default_conf_usdt):
|
||||
def test_MarketCapPairList_exceptions(mocker, default_conf_usdt, caplog):
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt)
|
||||
default_conf_usdt["pairlists"] = [{"method": "MarketCapPairList"}]
|
||||
with pytest.raises(OperationalException, match=r"`number_assets` not specified.*"):
|
||||
@@ -2458,13 +2458,11 @@ def test_MarketCapPairList_exceptions(mocker, default_conf_usdt):
|
||||
PairListManager(exchange, default_conf_usdt)
|
||||
|
||||
default_conf_usdt["pairlists"] = [
|
||||
{"method": "MarketCapPairList", "number_assets": 20, "max_rank": 260}
|
||||
{"method": "MarketCapPairList", "number_assets": 20, "max_rank": 500}
|
||||
]
|
||||
with pytest.raises(
|
||||
OperationalException, match="This filter only support marketcap rank up to 250."
|
||||
):
|
||||
with caplog.at_level(logging.WARNING):
|
||||
PairListManager(exchange, default_conf_usdt)
|
||||
|
||||
assert log_has_re("The max rank you have set \\(500\\) is quite high", caplog)
|
||||
# Test invalid coinmarkets list
|
||||
mocker.patch(
|
||||
"freqtrade.plugins.pairlist.MarketCapPairList.FtCoinGeckoApi.get_coins_categories_list",
|
||||
|
||||
@@ -2355,8 +2355,8 @@ def test_send_msg_exit_notification(default_conf, mocker) -> None:
|
||||
"*Direction:* `Long`\n"
|
||||
"*Amount:* `1333.33333333`\n"
|
||||
"*Open Rate:* `0.00075 ETH`\n"
|
||||
"*Current Rate:* `0.00032 ETH`\n"
|
||||
"*Exit Rate:* `0.00032 ETH`\n"
|
||||
"*Current Rate:* `0.0003201 ETH`\n"
|
||||
"*Exit Rate:* `0.0003201 ETH`\n"
|
||||
"*Duration:* `1:00:00 (60.0 min)`"
|
||||
)
|
||||
|
||||
@@ -2398,8 +2398,8 @@ def test_send_msg_exit_notification(default_conf, mocker) -> None:
|
||||
"*Direction:* `Long`\n"
|
||||
"*Amount:* `1333.33333333`\n"
|
||||
"*Open Rate:* `0.00075 ETH`\n"
|
||||
"*Current Rate:* `0.00032 ETH`\n"
|
||||
"*Exit Rate:* `0.00032 ETH`\n"
|
||||
"*Current Rate:* `0.0003201 ETH`\n"
|
||||
"*Exit Rate:* `0.0003201 ETH`\n"
|
||||
"*Remaining:* `0.01 ETH / -24.812 USD`"
|
||||
)
|
||||
|
||||
@@ -2437,8 +2437,8 @@ def test_send_msg_exit_notification(default_conf, mocker) -> None:
|
||||
"*Direction:* `Long`\n"
|
||||
"*Amount:* `1333.33333333`\n"
|
||||
"*Open Rate:* `0.00075 ETH`\n"
|
||||
"*Current Rate:* `0.00032 ETH`\n"
|
||||
"*Exit Rate:* `0.00032 ETH`\n"
|
||||
"*Current Rate:* `0.0003201 ETH`\n"
|
||||
"*Exit Rate:* `0.0003201 ETH`\n"
|
||||
"*Duration:* `1 day, 2:30:00 (1590.0 min)`"
|
||||
)
|
||||
# Reset singleton function to avoid random breaks
|
||||
@@ -2536,7 +2536,7 @@ def test_send_msg_exit_fill_notification(
|
||||
f"{leverage_text}"
|
||||
"*Amount:* `1333.33333333`\n"
|
||||
"*Open Rate:* `0.00075 ETH`\n"
|
||||
"*Exit Rate:* `0.00032 ETH`\n"
|
||||
"*Exit Rate:* `0.0003201 ETH`\n"
|
||||
"*Duration:* `1 day, 2:30:00 (1590.0 min)`"
|
||||
)
|
||||
|
||||
@@ -2686,8 +2686,8 @@ def test_send_msg_exit_notification_no_fiat(
|
||||
f"{leverage_text}`\n"
|
||||
"*Amount:* `1333.33333333`\n"
|
||||
"*Open Rate:* `0.00075 ETH`\n"
|
||||
"*Current Rate:* `0.00032 ETH`\n"
|
||||
"*Exit Rate:* `0.00032 ETH`\n"
|
||||
"*Current Rate:* `0.0003201 ETH`\n"
|
||||
"*Exit Rate:* `0.0003201 ETH`\n"
|
||||
"*Duration:* `2:35:03 (155.1 min)`"
|
||||
)
|
||||
|
||||
@@ -2885,3 +2885,85 @@ async def test_telegram_list_custom_data(default_conf_usdt, update, ticker, fee,
|
||||
) in msg_mock.call_args_list[2][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
|
||||
|
||||
def test_noficiation_settings(default_conf_usdt, mocker):
|
||||
(telegram, _, _) = get_telegram_testobject(mocker, default_conf_usdt)
|
||||
telegram._config["telegram"].update(
|
||||
{
|
||||
"notification_settings": {
|
||||
"status": "silent",
|
||||
"warning": "on",
|
||||
"startup": "off",
|
||||
"entry": "silent",
|
||||
"entry_fill": "on",
|
||||
"entry_cancel": "silent",
|
||||
"exit": {
|
||||
"roi": "silent",
|
||||
"emergency_exit": "on",
|
||||
"force_exit": "on",
|
||||
"exit_signal": "silent",
|
||||
"trailing_stop_loss": "on",
|
||||
"stop_loss": "on",
|
||||
"stoploss_on_exchange": "on",
|
||||
"custom_exit": "silent",
|
||||
"partial_exit": "off",
|
||||
},
|
||||
"exit_fill": {
|
||||
"roi": "silent",
|
||||
"partial_exit": "off",
|
||||
"*": "silent", # Default to silent
|
||||
},
|
||||
"exit_cancel": "on",
|
||||
"protection_trigger": "off",
|
||||
"protection_trigger_global": "on",
|
||||
"strategy_msg": "off",
|
||||
"show_candle": "off",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
loudness = telegram._message_loudness
|
||||
|
||||
assert loudness({"type": RPCMessageType.ENTRY, "exit_reason": ""}) == "silent"
|
||||
assert loudness({"type": RPCMessageType.ENTRY_FILL, "exit_reason": ""}) == "on"
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": ""}) == "on"
|
||||
# Default to silent due to "*" definition
|
||||
assert loudness({"type": RPCMessageType.EXIT_FILL, "exit_reason": ""}) == "silent"
|
||||
assert loudness({"type": RPCMessageType.PROTECTION_TRIGGER, "exit_reason": ""}) == "off"
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "roi"}) == "silent"
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "partial_exit"}) == "off"
|
||||
# Not given key defaults to on
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "cust_exit112"}) == "on"
|
||||
|
||||
assert loudness({"type": RPCMessageType.EXIT_FILL, "exit_reason": "roi"}) == "silent"
|
||||
assert loudness({"type": RPCMessageType.EXIT_FILL, "exit_reason": "partial_exit"}) == "off"
|
||||
# Default to silent due to "*" definition
|
||||
assert loudness({"type": RPCMessageType.EXIT_FILL, "exit_reason": "cust_exit112"}) == "silent"
|
||||
|
||||
# Simplified setup for exit
|
||||
telegram._config["telegram"].update(
|
||||
{
|
||||
"notification_settings": {
|
||||
"status": "silent",
|
||||
"warning": "on",
|
||||
"startup": "off",
|
||||
"entry": "silent",
|
||||
"entry_fill": "on",
|
||||
"entry_cancel": "silent",
|
||||
"exit": "off",
|
||||
"exit_cancel": "on",
|
||||
"exit_fill": "on",
|
||||
"protection_trigger": "off",
|
||||
"protection_trigger_global": "on",
|
||||
"strategy_msg": "off",
|
||||
"show_candle": "off",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
assert loudness({"type": RPCMessageType.EXIT_FILL, "exit_reason": "roi"}) == "on"
|
||||
# All regular exits are off
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "roi"}) == "off"
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "partial_exit"}) == "off"
|
||||
assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "cust_exit112"}) == "off"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
@@ -175,7 +174,7 @@ class StrategyTestV3(IStrategy):
|
||||
current_rate: float,
|
||||
proposed_leverage: float,
|
||||
max_leverage: float,
|
||||
entry_tag: Optional[str],
|
||||
entry_tag: str | None,
|
||||
side: str,
|
||||
**kwargs,
|
||||
) -> float:
|
||||
@@ -190,14 +189,14 @@ class StrategyTestV3(IStrategy):
|
||||
current_time: datetime,
|
||||
current_rate: float,
|
||||
current_profit: float,
|
||||
min_stake: Optional[float],
|
||||
min_stake: float | None,
|
||||
max_stake: float,
|
||||
current_entry_rate: float,
|
||||
current_exit_rate: float,
|
||||
current_entry_profit: float,
|
||||
current_exit_profit: float,
|
||||
**kwargs,
|
||||
) -> Optional[float]:
|
||||
) -> float | None:
|
||||
if current_profit < -0.0075:
|
||||
orders = trade.select_filled_orders(trade.entry_side)
|
||||
return round(orders[0].stake_amount, 0)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pandas import DataFrame
|
||||
from strategy_test_v3 import StrategyTestV3
|
||||
@@ -34,10 +33,10 @@ class StrategyTestV3CustomEntryPrice(StrategyTestV3):
|
||||
def custom_entry_price(
|
||||
self,
|
||||
pair: str,
|
||||
trade: Optional[Trade],
|
||||
trade: Trade | None,
|
||||
current_time: datetime,
|
||||
proposed_rate: float,
|
||||
entry_tag: Optional[str],
|
||||
entry_tag: str | None,
|
||||
side: str,
|
||||
**kwargs,
|
||||
) -> float:
|
||||
|
||||
@@ -489,7 +489,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog, tmp_pa
|
||||
"--timeframe",
|
||||
"1m",
|
||||
"--enable-position-stacking",
|
||||
"--disable-max-market-positions",
|
||||
"--timerange",
|
||||
":100",
|
||||
"--export",
|
||||
@@ -518,10 +517,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog, tmp_pa
|
||||
assert "position_stacking" in config
|
||||
assert log_has("Parameter --enable-position-stacking detected ...", caplog)
|
||||
|
||||
assert "use_max_market_positions" in config
|
||||
assert log_has("Parameter --disable-max-market-positions detected ...", caplog)
|
||||
assert log_has("max_open_trades set to unlimited ...", caplog)
|
||||
|
||||
assert "timerange" in config
|
||||
assert log_has("Parameter --timerange detected: {} ...".format(config["timerange"]), caplog)
|
||||
|
||||
@@ -570,8 +565,6 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
|
||||
|
||||
assert "position_stacking" not in config
|
||||
|
||||
assert "use_max_market_positions" not in config
|
||||
|
||||
assert "timerange" not in config
|
||||
|
||||
assert "export" in config
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from freqtrade.util import decimals_per_coin, fmt_coin, round_value
|
||||
from freqtrade.util.formatters import fmt_coin2
|
||||
|
||||
|
||||
def test_decimals_per_coin():
|
||||
@@ -25,6 +26,16 @@ def test_fmt_coin():
|
||||
assert fmt_coin(222.2, "USDT", False, True) == "222.200"
|
||||
|
||||
|
||||
def test_fmt_coin2():
|
||||
assert fmt_coin2(222.222222, "USDT") == "222.222222 USDT"
|
||||
assert fmt_coin2(222.2, "XRP", 3, keep_trailing_zeros=True) == "222.200 XRP"
|
||||
assert fmt_coin2(222.2, "USDT") == "222.2 USDT"
|
||||
assert fmt_coin2(222.12745, "EUR") == "222.12745 EUR"
|
||||
assert fmt_coin2(0.1274512123, "BTC") == "0.12745121 BTC"
|
||||
assert fmt_coin2(0.1274512123, "ETH") == "0.12745121 ETH"
|
||||
assert fmt_coin2(0.00001245, "PEPE") == "0.00001245 PEPE"
|
||||
|
||||
|
||||
def test_round_value():
|
||||
assert round_value(222.222222, 3) == "222.222"
|
||||
assert round_value(222.2, 3) == "222.2"
|
||||
|
||||
Reference in New Issue
Block a user