Merge branch 'develop' into feature/proceed-exit-while-open-order
This commit is contained in:
@@ -4,10 +4,10 @@ from unittest.mock import MagicMock
|
||||
import pytest
|
||||
import rapidjson
|
||||
|
||||
from freqtrade.commands.build_config_commands import (
|
||||
from freqtrade.commands.build_config_commands import start_new_config
|
||||
from freqtrade.configuration.deploy_config import (
|
||||
ask_user_config,
|
||||
ask_user_overwrite,
|
||||
start_new_config,
|
||||
validate_is_float,
|
||||
validate_is_int,
|
||||
)
|
||||
@@ -39,7 +39,7 @@ def test_start_new_config(mocker, caplog, exchange):
|
||||
wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
unlink_mock = mocker.patch.object(Path, "unlink", MagicMock())
|
||||
mocker.patch("freqtrade.commands.build_config_commands.ask_user_overwrite", return_value=True)
|
||||
mocker.patch("freqtrade.configuration.deploy_config.ask_user_overwrite", return_value=True)
|
||||
|
||||
sample_selections = {
|
||||
"max_open_trades": 3,
|
||||
@@ -62,7 +62,7 @@ def test_start_new_config(mocker, caplog, exchange):
|
||||
"api_server_password": "MoneyMachine",
|
||||
}
|
||||
mocker.patch(
|
||||
"freqtrade.commands.build_config_commands.ask_user_config", return_value=sample_selections
|
||||
"freqtrade.configuration.deploy_config.ask_user_config", return_value=sample_selections
|
||||
)
|
||||
args = ["new-config", "--config", "coolconfig.json"]
|
||||
start_new_config(get_args(args))
|
||||
@@ -80,7 +80,7 @@ def test_start_new_config(mocker, caplog, exchange):
|
||||
|
||||
def test_start_new_config_exists(mocker, caplog):
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
mocker.patch("freqtrade.commands.build_config_commands.ask_user_overwrite", return_value=False)
|
||||
mocker.patch("freqtrade.configuration.deploy_config.ask_user_overwrite", return_value=False)
|
||||
args = ["new-config", "--config", "coolconfig.json"]
|
||||
with pytest.raises(OperationalException, match=r"Configuration .* already exists\."):
|
||||
start_new_config(get_args(args))
|
||||
@@ -91,14 +91,14 @@ def test_ask_user_overwrite(mocker):
|
||||
Once https://github.com/tmbo/questionary/issues/35 is implemented, improve this test.
|
||||
"""
|
||||
prompt_mock = mocker.patch(
|
||||
"freqtrade.commands.build_config_commands.prompt", return_value={"overwrite": False}
|
||||
"freqtrade.configuration.deploy_config.prompt", return_value={"overwrite": False}
|
||||
)
|
||||
assert not ask_user_overwrite(Path("test.json"))
|
||||
assert prompt_mock.call_count == 1
|
||||
|
||||
prompt_mock.reset_mock()
|
||||
prompt_mock = mocker.patch(
|
||||
"freqtrade.commands.build_config_commands.prompt", return_value={"overwrite": True}
|
||||
"freqtrade.configuration.deploy_config.prompt", return_value={"overwrite": True}
|
||||
)
|
||||
assert ask_user_overwrite(Path("test.json"))
|
||||
assert prompt_mock.call_count == 1
|
||||
@@ -109,13 +109,13 @@ def test_ask_user_config(mocker):
|
||||
Once https://github.com/tmbo/questionary/issues/35 is implemented, improve this test.
|
||||
"""
|
||||
prompt_mock = mocker.patch(
|
||||
"freqtrade.commands.build_config_commands.prompt", return_value={"overwrite": False}
|
||||
"freqtrade.configuration.deploy_config.prompt", return_value={"overwrite": False}
|
||||
)
|
||||
answers = ask_user_config()
|
||||
assert isinstance(answers, dict)
|
||||
assert prompt_mock.call_count == 1
|
||||
|
||||
prompt_mock = mocker.patch("freqtrade.commands.build_config_commands.prompt", return_value={})
|
||||
prompt_mock = mocker.patch("freqtrade.configuration.deploy_config.prompt", return_value={})
|
||||
|
||||
with pytest.raises(OperationalException, match=r"User interrupted interactive questions\."):
|
||||
ask_user_config()
|
||||
|
||||
@@ -31,7 +31,7 @@ from freqtrade.commands import (
|
||||
start_webserver,
|
||||
)
|
||||
from freqtrade.commands.db_commands import start_convert_db
|
||||
from freqtrade.commands.deploy_commands import (
|
||||
from freqtrade.commands.deploy_ui import (
|
||||
clean_ui_subdir,
|
||||
download_and_install_ui,
|
||||
get_ui_download_url,
|
||||
@@ -571,8 +571,12 @@ def test_create_datadir_failed(caplog):
|
||||
|
||||
|
||||
def test_create_datadir(caplog, mocker):
|
||||
cud = mocker.patch("freqtrade.commands.deploy_commands.create_userdata_dir", MagicMock())
|
||||
csf = mocker.patch("freqtrade.commands.deploy_commands.copy_sample_files", MagicMock())
|
||||
cud = mocker.patch(
|
||||
"freqtrade.configuration.directory_operations.create_userdata_dir", MagicMock()
|
||||
)
|
||||
csf = mocker.patch(
|
||||
"freqtrade.configuration.directory_operations.copy_sample_files", MagicMock()
|
||||
)
|
||||
args = ["create-userdir", "--userdir", "/temp/freqtrade/test"]
|
||||
start_create_userdir(get_args(args))
|
||||
|
||||
@@ -591,7 +595,7 @@ def test_start_new_strategy(mocker, caplog):
|
||||
assert "CoolNewStrategy" in wt_mock.call_args_list[0][0][0]
|
||||
assert log_has_re("Writing strategy to .*", caplog)
|
||||
|
||||
mocker.patch("freqtrade.commands.deploy_commands.setup_utils_configuration")
|
||||
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\."
|
||||
@@ -608,13 +612,13 @@ def test_start_new_strategy_no_arg(mocker, caplog):
|
||||
|
||||
|
||||
def test_start_install_ui(mocker):
|
||||
clean_mock = mocker.patch("freqtrade.commands.deploy_commands.clean_ui_subdir")
|
||||
clean_mock = mocker.patch("freqtrade.commands.deploy_ui.clean_ui_subdir")
|
||||
get_url_mock = mocker.patch(
|
||||
"freqtrade.commands.deploy_commands.get_ui_download_url",
|
||||
"freqtrade.commands.deploy_ui.get_ui_download_url",
|
||||
return_value=("https://example.com/whatever", "0.0.1"),
|
||||
)
|
||||
download_mock = mocker.patch("freqtrade.commands.deploy_commands.download_and_install_ui")
|
||||
mocker.patch("freqtrade.commands.deploy_commands.read_ui_version", return_value=None)
|
||||
download_mock = mocker.patch("freqtrade.commands.deploy_ui.download_and_install_ui")
|
||||
mocker.patch("freqtrade.commands.deploy_ui.read_ui_version", return_value=None)
|
||||
args = [
|
||||
"install-ui",
|
||||
]
|
||||
@@ -638,13 +642,13 @@ def test_start_install_ui(mocker):
|
||||
|
||||
|
||||
def test_clean_ui_subdir(mocker, tmp_path, caplog):
|
||||
mocker.patch("freqtrade.commands.deploy_commands.Path.is_dir", side_effect=[True, True])
|
||||
mocker.patch("freqtrade.commands.deploy_commands.Path.is_file", side_effect=[False, True])
|
||||
rd_mock = mocker.patch("freqtrade.commands.deploy_commands.Path.rmdir")
|
||||
ul_mock = mocker.patch("freqtrade.commands.deploy_commands.Path.unlink")
|
||||
mocker.patch("freqtrade.commands.deploy_ui.Path.is_dir", side_effect=[True, True])
|
||||
mocker.patch("freqtrade.commands.deploy_ui.Path.is_file", side_effect=[False, True])
|
||||
rd_mock = mocker.patch("freqtrade.commands.deploy_ui.Path.rmdir")
|
||||
ul_mock = mocker.patch("freqtrade.commands.deploy_ui.Path.unlink")
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.commands.deploy_commands.Path.glob",
|
||||
"freqtrade.commands.deploy_ui.Path.glob",
|
||||
return_value=[Path("test1"), Path("test2"), Path(".gitkeep")],
|
||||
)
|
||||
folder = tmp_path / "uitests"
|
||||
@@ -664,10 +668,10 @@ def test_download_and_install_ui(mocker, tmp_path):
|
||||
file_like_object.seek(0)
|
||||
requests_mock.content = file_like_object.read()
|
||||
|
||||
mocker.patch("freqtrade.commands.deploy_commands.requests.get", return_value=requests_mock)
|
||||
mocker.patch("freqtrade.commands.deploy_ui.requests.get", return_value=requests_mock)
|
||||
|
||||
mocker.patch("freqtrade.commands.deploy_commands.Path.is_dir", side_effect=[True, False])
|
||||
wb_mock = mocker.patch("freqtrade.commands.deploy_commands.Path.write_bytes")
|
||||
mocker.patch("freqtrade.commands.deploy_ui.Path.is_dir", side_effect=[True, False])
|
||||
wb_mock = mocker.patch("freqtrade.commands.deploy_ui.Path.write_bytes")
|
||||
|
||||
folder = tmp_path / "uitests_dl"
|
||||
folder.mkdir(exist_ok=True)
|
||||
@@ -689,9 +693,7 @@ def test_get_ui_download_url(mocker):
|
||||
[{"browser_download_url": "http://download.zip"}],
|
||||
]
|
||||
)
|
||||
get_mock = mocker.patch(
|
||||
"freqtrade.commands.deploy_commands.requests.get", return_value=response
|
||||
)
|
||||
get_mock = mocker.patch("freqtrade.commands.deploy_ui.requests.get", return_value=response)
|
||||
x, last_version = get_ui_download_url()
|
||||
assert get_mock.call_count == 2
|
||||
assert last_version == "0.0.1"
|
||||
@@ -714,9 +716,7 @@ def test_get_ui_download_url_direct(mocker):
|
||||
},
|
||||
]
|
||||
)
|
||||
get_mock = mocker.patch(
|
||||
"freqtrade.commands.deploy_commands.requests.get", return_value=response
|
||||
)
|
||||
get_mock = mocker.patch("freqtrade.commands.deploy_ui.requests.get", return_value=response)
|
||||
x, last_version = get_ui_download_url()
|
||||
assert get_mock.call_count == 1
|
||||
assert last_version == "0.0.2"
|
||||
@@ -734,7 +734,7 @@ def test_get_ui_download_url_direct(mocker):
|
||||
|
||||
def test_download_data_keyboardInterrupt(mocker, markets):
|
||||
dl_mock = mocker.patch(
|
||||
"freqtrade.commands.data_commands.download_data_main",
|
||||
"freqtrade.data.history.download_data_main",
|
||||
MagicMock(side_effect=KeyboardInterrupt),
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
@@ -972,7 +972,7 @@ def test_download_data_data_invalid(mocker):
|
||||
|
||||
def test_start_convert_trades(mocker):
|
||||
convert_mock = mocker.patch(
|
||||
"freqtrade.commands.data_commands.convert_trades_to_ohlcv", MagicMock(return_value=[])
|
||||
"freqtrade.data.converter.convert_trades_to_ohlcv", MagicMock(return_value=[])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(f"{EXMS}.get_markets")
|
||||
@@ -1522,7 +1522,7 @@ def test_hyperopt_show(mocker, capsys):
|
||||
mocker.patch(
|
||||
"freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results", side_effect=fake_iterator
|
||||
)
|
||||
mocker.patch("freqtrade.commands.hyperopt_commands.show_backtest_result")
|
||||
mocker.patch("freqtrade.optimize.optimize_reports.show_backtest_result")
|
||||
|
||||
args = [
|
||||
"hyperopt-show",
|
||||
@@ -1579,8 +1579,8 @@ def test_hyperopt_show(mocker, capsys):
|
||||
|
||||
|
||||
def test_convert_data(mocker, testdatadir):
|
||||
ohlcv_mock = mocker.patch("freqtrade.commands.data_commands.convert_ohlcv_format")
|
||||
trades_mock = mocker.patch("freqtrade.commands.data_commands.convert_trades_format")
|
||||
ohlcv_mock = mocker.patch("freqtrade.data.converter.convert_ohlcv_format")
|
||||
trades_mock = mocker.patch("freqtrade.data.converter.convert_trades_format")
|
||||
args = [
|
||||
"convert-data",
|
||||
"--format-from",
|
||||
@@ -1601,8 +1601,8 @@ def test_convert_data(mocker, testdatadir):
|
||||
|
||||
|
||||
def test_convert_data_trades(mocker, testdatadir):
|
||||
ohlcv_mock = mocker.patch("freqtrade.commands.data_commands.convert_ohlcv_format")
|
||||
trades_mock = mocker.patch("freqtrade.commands.data_commands.convert_trades_format")
|
||||
ohlcv_mock = mocker.patch("freqtrade.data.converter.convert_ohlcv_format")
|
||||
trades_mock = mocker.patch("freqtrade.data.converter.convert_trades_format")
|
||||
args = [
|
||||
"convert-trade-data",
|
||||
"--format-from",
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
MAXIMUM_STARTUP_TIME = 0.5
|
||||
|
||||
|
||||
def test_startup_time():
|
||||
# warm up to generate pyc
|
||||
subprocess.run(["freqtrade", "-h"])
|
||||
|
||||
start = time.time()
|
||||
subprocess.run(["freqtrade", "-h"])
|
||||
elapsed = time.time() - start
|
||||
assert (
|
||||
elapsed < MAXIMUM_STARTUP_TIME
|
||||
), "The startup time is too long, try to use lazy import in the command entry function"
|
||||
@@ -7,6 +7,7 @@ import pytest
|
||||
|
||||
from freqtrade.enums import CandleType, MarginMode, TradingMode
|
||||
from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException
|
||||
from freqtrade.persistence import Trade
|
||||
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has_re
|
||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||
|
||||
@@ -171,59 +172,101 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"is_short, trading_mode, margin_mode, wallet_balance, "
|
||||
"mm_ex_1, upnl_ex_1, maintenance_amt, amount, open_rate, "
|
||||
"pair, is_short, trading_mode, margin_mode, wallet_balance, "
|
||||
"maintenance_amt, amount, open_rate, open_trades,"
|
||||
"mm_ratio, expected",
|
||||
[
|
||||
(
|
||||
"ETH/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"isolated",
|
||||
1535443.01,
|
||||
0.0,
|
||||
0.0,
|
||||
135365.00,
|
||||
3683.979,
|
||||
1456.84,
|
||||
[],
|
||||
0.10,
|
||||
1114.78,
|
||||
),
|
||||
(
|
||||
"ETH/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"isolated",
|
||||
1535443.01,
|
||||
0.0,
|
||||
0.0,
|
||||
16300.000,
|
||||
109.488,
|
||||
32481.980,
|
||||
[],
|
||||
0.025,
|
||||
18778.73,
|
||||
),
|
||||
(
|
||||
"ETH/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"cross",
|
||||
1535443.01,
|
||||
71200.81144,
|
||||
-56354.57,
|
||||
135365.00,
|
||||
3683.979,
|
||||
1456.84,
|
||||
3683.979, # amount
|
||||
1456.84, # open_rate
|
||||
[
|
||||
{
|
||||
# From calc example
|
||||
"pair": "BTC/USDT:USDT",
|
||||
"open_rate": 32481.98,
|
||||
"amount": 109.488,
|
||||
"stake_amount": 3556387.02624, # open_rate * amount
|
||||
"mark_price": 31967.27,
|
||||
"mm_ratio": 0.025,
|
||||
"maintenance_amt": 16300.0,
|
||||
},
|
||||
{
|
||||
# From calc example
|
||||
"pair": "ETH/USDT:USDT",
|
||||
"open_rate": 1456.84,
|
||||
"amount": 3683.979,
|
||||
"stake_amount": 5366967.96,
|
||||
"mark_price": 1335.18,
|
||||
"mm_ratio": 0.10,
|
||||
"maintenance_amt": 135365.00,
|
||||
},
|
||||
],
|
||||
0.10,
|
||||
1153.26,
|
||||
),
|
||||
(
|
||||
"BTC/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"cross",
|
||||
1535443.01,
|
||||
356512.508,
|
||||
-448192.89,
|
||||
16300.000,
|
||||
109.488,
|
||||
32481.980,
|
||||
16300.0,
|
||||
109.488, # amount
|
||||
32481.980, # open_rate
|
||||
[
|
||||
{
|
||||
# From calc example
|
||||
"pair": "BTC/USDT:USDT",
|
||||
"open_rate": 32481.98,
|
||||
"amount": 109.488,
|
||||
"stake_amount": 3556387.02624, # open_rate * amount
|
||||
"mark_price": 31967.27,
|
||||
"mm_ratio": 0.025,
|
||||
"maintenance_amt": 16300.0,
|
||||
},
|
||||
{
|
||||
# From calc example
|
||||
"pair": "ETH/USDT:USDT",
|
||||
"open_rate": 1456.84,
|
||||
"amount": 3683.979,
|
||||
"stake_amount": 5366967.96,
|
||||
"mark_price": 1335.18,
|
||||
"mm_ratio": 0.10,
|
||||
"maintenance_amt": 135365.00,
|
||||
},
|
||||
],
|
||||
0.025,
|
||||
26316.89,
|
||||
),
|
||||
@@ -232,15 +275,15 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
|
||||
def test_liquidation_price_binance(
|
||||
mocker,
|
||||
default_conf,
|
||||
open_rate,
|
||||
pair,
|
||||
is_short,
|
||||
trading_mode,
|
||||
margin_mode,
|
||||
wallet_balance,
|
||||
mm_ex_1,
|
||||
upnl_ex_1,
|
||||
maintenance_amt,
|
||||
amount,
|
||||
open_rate,
|
||||
open_trades,
|
||||
mm_ratio,
|
||||
expected,
|
||||
):
|
||||
@@ -248,20 +291,48 @@ def test_liquidation_price_binance(
|
||||
default_conf["margin_mode"] = margin_mode
|
||||
default_conf["liquidation_buffer"] = 0.0
|
||||
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
|
||||
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(mm_ratio, maintenance_amt))
|
||||
|
||||
def get_maint_ratio(pair_, stake_amount):
|
||||
if pair_ != pair:
|
||||
oc = [c for c in open_trades if c["pair"] == pair_][0]
|
||||
return oc["mm_ratio"], oc["maintenance_amt"]
|
||||
return mm_ratio, maintenance_amt
|
||||
|
||||
def fetch_funding_rates(*args, **kwargs):
|
||||
return {
|
||||
t["pair"]: {
|
||||
"symbol": t["pair"],
|
||||
"markPrice": t["mark_price"],
|
||||
}
|
||||
for t in open_trades
|
||||
}
|
||||
|
||||
exchange.get_maintenance_ratio_and_amt = get_maint_ratio
|
||||
exchange.fetch_funding_rates = fetch_funding_rates
|
||||
|
||||
open_trade_objects = [
|
||||
Trade(
|
||||
pair=t["pair"],
|
||||
open_rate=t["open_rate"],
|
||||
amount=t["amount"],
|
||||
stake_amount=t["stake_amount"],
|
||||
fee_open=0,
|
||||
)
|
||||
for t in open_trades
|
||||
]
|
||||
|
||||
assert (
|
||||
pytest.approx(
|
||||
round(
|
||||
exchange.get_liquidation_price(
|
||||
pair="DOGE/USDT",
|
||||
pair=pair,
|
||||
open_rate=open_rate,
|
||||
is_short=is_short,
|
||||
wallet_balance=wallet_balance,
|
||||
mm_ex_1=mm_ex_1,
|
||||
upnl_ex_1=upnl_ex_1,
|
||||
amount=amount,
|
||||
stake_amount=open_rate * amount,
|
||||
leverage=5,
|
||||
open_trades=open_trade_objects,
|
||||
),
|
||||
2,
|
||||
)
|
||||
|
||||
@@ -5524,8 +5524,6 @@ def test_liquidation_price_is_none(
|
||||
stake_amount=open_rate * 71200.81144,
|
||||
leverage=5,
|
||||
wallet_balance=-56354.57,
|
||||
mm_ex_1=0.10,
|
||||
upnl_ex_1=0.0,
|
||||
)
|
||||
is None
|
||||
)
|
||||
@@ -6011,6 +6009,7 @@ def test_get_liquidation_price1(mocker, default_conf):
|
||||
stake_amount=18.884 * 0.8,
|
||||
leverage=leverage,
|
||||
wallet_balance=18.884 * 0.8,
|
||||
open_trades=[],
|
||||
)
|
||||
|
||||
|
||||
@@ -6141,6 +6140,7 @@ def test_get_liquidation_price(
|
||||
wallet_balance=amount * open_rate / leverage,
|
||||
leverage=leverage,
|
||||
is_short=is_short,
|
||||
open_trades=[],
|
||||
)
|
||||
if expected_liq is None:
|
||||
assert liq is None
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -10,8 +9,8 @@ from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||
from tests.conftest import EXMS, get_default_conf_usdt
|
||||
|
||||
|
||||
EXCHANGE_FIXTURE_TYPE = Tuple[Exchange, str]
|
||||
EXCHANGE_WS_FIXTURE_TYPE = Tuple[Exchange, str, str]
|
||||
EXCHANGE_FIXTURE_TYPE = tuple[Exchange, str]
|
||||
EXCHANGE_WS_FIXTURE_TYPE = tuple[Exchange, str, str]
|
||||
|
||||
|
||||
# Exchanges that should be tested online
|
||||
|
||||
@@ -457,6 +457,7 @@ class TestCCXTExchange:
|
||||
stake_amount=100,
|
||||
leverage=5,
|
||||
wallet_balance=100,
|
||||
open_trades=[],
|
||||
)
|
||||
assert isinstance(liquidation_price, float)
|
||||
assert liquidation_price >= 0.0
|
||||
@@ -469,6 +470,7 @@ class TestCCXTExchange:
|
||||
stake_amount=100,
|
||||
leverage=5,
|
||||
wallet_balance=100,
|
||||
open_trades=[],
|
||||
)
|
||||
assert isinstance(liquidation_price, float)
|
||||
assert liquidation_price >= 0.0
|
||||
|
||||
@@ -2,7 +2,7 @@ import platform
|
||||
import sys
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
@@ -112,7 +112,7 @@ def make_rl_config(conf):
|
||||
return conf
|
||||
|
||||
|
||||
def mock_pytorch_mlp_model_training_parameters() -> Dict[str, Any]:
|
||||
def mock_pytorch_mlp_model_training_parameters() -> dict[str, Any]:
|
||||
return {
|
||||
"learning_rate": 3e-4,
|
||||
"trainer_kwargs": {
|
||||
|
||||
@@ -5,7 +5,6 @@ import logging
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from datetime import timedelta
|
||||
from typing import List
|
||||
from unittest.mock import ANY, MagicMock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -5443,7 +5442,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
||||
assert trade.amount == 10
|
||||
assert trade.stake_amount == 110
|
||||
assert not trade.fee_updated("buy")
|
||||
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||
trades: list[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||
assert len(trades) == 1
|
||||
assert trade.is_open
|
||||
assert not trade.fee_updated("buy")
|
||||
@@ -5469,7 +5468,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
||||
assert orders
|
||||
assert len(orders) == 2
|
||||
# Assert that the trade is found as open and without fees
|
||||
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||
trades: list[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||
assert len(trades) == 1
|
||||
# Assert trade is as expected
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
@@ -5526,7 +5525,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
||||
assert order.order_id == "651"
|
||||
|
||||
# Assert that the trade is not found as open and without fees
|
||||
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||
trades: list[Trade] = Trade.get_open_trades_without_assigned_fees()
|
||||
assert len(trades) == 1
|
||||
|
||||
# Add a second DCA
|
||||
@@ -5726,7 +5725,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
||||
exit_check=ExitCheckTuple(exit_type=ExitType.PARTIAL_EXIT),
|
||||
sub_trade_amt=amount,
|
||||
)
|
||||
trades: List[Trade] = trade.get_open_trades_without_assigned_fees()
|
||||
trades: list[Trade] = trade.get_open_trades_without_assigned_fees()
|
||||
assert len(trades) == 1
|
||||
# Assert trade is as expected (averaged dca)
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.enums.marginmode import MarginMode
|
||||
from freqtrade.leverage.liquidation_price import update_liquidation_prices
|
||||
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [False, True])
|
||||
@pytest.mark.parametrize("margin_mode", [MarginMode.CROSS, MarginMode.ISOLATED])
|
||||
def test_update_liquidation_prices(mocker, margin_mode, dry_run):
|
||||
# Heavily mocked test - Only testing the logic of the function
|
||||
# update liquidation price for trade in isolated mode
|
||||
# update liquidation price for all trades in cross mode
|
||||
exchange = MagicMock()
|
||||
exchange.margin_mode = margin_mode
|
||||
wallets = MagicMock()
|
||||
trade_mock = MagicMock()
|
||||
|
||||
mocker.patch("freqtrade.persistence.Trade.get_open_trades", return_value=[trade_mock])
|
||||
|
||||
update_liquidation_prices(
|
||||
trade=trade_mock,
|
||||
exchange=exchange,
|
||||
wallets=wallets,
|
||||
stake_currency="USDT",
|
||||
dry_run=dry_run,
|
||||
)
|
||||
|
||||
assert trade_mock.set_liquidation_price.call_count == 1
|
||||
|
||||
assert wallets.get_total.call_count == (
|
||||
0 if margin_mode == MarginMode.ISOLATED or not dry_run else 1
|
||||
)
|
||||
|
||||
# Test with multiple trades
|
||||
trade_mock.reset_mock()
|
||||
trade_mock_2 = MagicMock()
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.persistence.Trade.get_open_trades", return_value=[trade_mock, trade_mock_2]
|
||||
)
|
||||
|
||||
update_liquidation_prices(
|
||||
trade=trade_mock,
|
||||
exchange=exchange,
|
||||
wallets=wallets,
|
||||
stake_currency="USDT",
|
||||
dry_run=dry_run,
|
||||
)
|
||||
# Trade2 is only updated in cross mode
|
||||
assert trade_mock_2.set_liquidation_price.call_count == (
|
||||
1 if margin_mode == MarginMode.CROSS else 0
|
||||
)
|
||||
assert trade_mock.set_liquidation_price.call_count == 1
|
||||
|
||||
assert wallets.call_count == 0 if not dry_run else 1
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import timedelta
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
from typing import NamedTuple, Optional
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
@@ -29,10 +29,10 @@ class BTContainer(NamedTuple):
|
||||
Minimal BacktestContainer defining Backtest inputs and results.
|
||||
"""
|
||||
|
||||
data: List[List[float]]
|
||||
data: list[list[float]]
|
||||
stop_loss: float
|
||||
roi: Dict[str, float]
|
||||
trades: List[BTrade]
|
||||
roi: dict[str, float]
|
||||
trades: list[BTrade]
|
||||
profit_perc: float
|
||||
trailing_stop: bool = False
|
||||
trailing_only_offset_is_reached: bool = False
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
@@ -14,7 +13,7 @@ from tests.conftest import CURRENT_TEST_STRATEGY, log_has, log_has_re
|
||||
|
||||
|
||||
# Functions for recurrent object patching
|
||||
def create_results() -> List[Dict]:
|
||||
def create_results() -> list[dict]:
|
||||
return [{"loss": 1, "result": "foo", "params": {}, "is_best": True}]
|
||||
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ def test_api_ui_fallback(botclient, mocker):
|
||||
def test_api_ui_version(botclient, mocker):
|
||||
_ftbot, client = botclient
|
||||
|
||||
mocker.patch("freqtrade.commands.deploy_commands.read_ui_version", return_value="0.1.2")
|
||||
mocker.patch("freqtrade.commands.deploy_ui.read_ui_version", return_value="0.1.2")
|
||||
rc = client_get(client, "/ui_version")
|
||||
assert rc.status_code == 200
|
||||
assert rc.json()["version"] == "0.1.2"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
from typing import Dict
|
||||
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
@@ -26,19 +25,19 @@ class freqai_rl_test_strat(IStrategy):
|
||||
can_short = False
|
||||
|
||||
def feature_engineering_expand_all(
|
||||
self, dataframe: DataFrame, period: int, metadata: Dict, **kwargs
|
||||
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
||||
):
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-pct-change"] = dataframe["close"].pct_change()
|
||||
dataframe["%-raw_volume"] = dataframe["volume"]
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
||||
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
|
||||
|
||||
@@ -49,7 +48,7 @@ class freqai_rl_test_strat(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["&-action"] = 0
|
||||
|
||||
return dataframe
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
from typing import Dict
|
||||
|
||||
import numpy as np
|
||||
import talib.abstract as ta
|
||||
@@ -58,7 +57,7 @@ class freqai_test_classifier(IStrategy):
|
||||
return informative_pairs
|
||||
|
||||
def feature_engineering_expand_all(
|
||||
self, dataframe: DataFrame, period: int, metadata: Dict, **kwargs
|
||||
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
||||
):
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
|
||||
@@ -66,20 +65,20 @@ class freqai_test_classifier(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-pct-change"] = dataframe["close"].pct_change()
|
||||
dataframe["%-raw_volume"] = dataframe["volume"]
|
||||
dataframe["%-raw_price"] = dataframe["close"]
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
||||
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
|
||||
|
||||
return dataframe
|
||||
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
self.freqai.class_names = ["down", "up"]
|
||||
dataframe["&s-up_or_down"] = np.where(
|
||||
dataframe["close"].shift(-100) > dataframe["close"], "up", "down"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
from typing import Dict
|
||||
|
||||
import numpy as np
|
||||
import talib.abstract as ta
|
||||
@@ -45,7 +44,7 @@ class freqai_test_multimodel_classifier_strat(IStrategy):
|
||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
||||
|
||||
def feature_engineering_expand_all(
|
||||
self, dataframe: DataFrame, period: int, metadata: Dict, **kwargs
|
||||
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
||||
):
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
|
||||
@@ -53,20 +52,20 @@ class freqai_test_multimodel_classifier_strat(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-pct-change"] = dataframe["close"].pct_change()
|
||||
dataframe["%-raw_volume"] = dataframe["volume"]
|
||||
dataframe["%-raw_price"] = dataframe["close"]
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
||||
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
|
||||
|
||||
return dataframe
|
||||
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["&s-up_or_down"] = np.where(
|
||||
dataframe["close"].shift(-50) > dataframe["close"], "up", "down"
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
from typing import Dict
|
||||
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
@@ -44,7 +43,7 @@ class freqai_test_multimodel_strat(IStrategy):
|
||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
||||
|
||||
def feature_engineering_expand_all(
|
||||
self, dataframe: DataFrame, period: int, metadata: Dict, **kwargs
|
||||
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
||||
):
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
|
||||
@@ -52,20 +51,20 @@ class freqai_test_multimodel_strat(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-pct-change"] = dataframe["close"].pct_change()
|
||||
dataframe["%-raw_volume"] = dataframe["volume"]
|
||||
dataframe["%-raw_price"] = dataframe["close"]
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
||||
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
|
||||
|
||||
return dataframe
|
||||
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["&-s_close"] = (
|
||||
dataframe["close"]
|
||||
.shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
from typing import Dict
|
||||
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
@@ -44,7 +43,7 @@ class freqai_test_strat(IStrategy):
|
||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
||||
|
||||
def feature_engineering_expand_all(
|
||||
self, dataframe: DataFrame, period: int, metadata: Dict, **kwargs
|
||||
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
||||
):
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
|
||||
@@ -52,20 +51,20 @@ class freqai_test_strat(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-pct-change"] = dataframe["close"].pct_change()
|
||||
dataframe["%-raw_volume"] = dataframe["volume"]
|
||||
dataframe["%-raw_price"] = dataframe["close"]
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
||||
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
|
||||
|
||||
return dataframe
|
||||
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs):
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs):
|
||||
dataframe["&-s_close"] = (
|
||||
dataframe["close"]
|
||||
.shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
|
||||
|
||||
@@ -33,6 +33,9 @@ class strategy_test_v3_recursive_issue(IStrategy):
|
||||
# Has both bias1 and bias2
|
||||
dataframe["rsi_lookahead"] = ta.RSI(dataframe, timeperiod=50).shift(-1)
|
||||
|
||||
# String columns shouldn't cause issues
|
||||
dataframe["test_string_column"] = f"a{len(dataframe)}"
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
|
||||
@@ -296,13 +296,6 @@ def test_assert_df(ohlcv_history, caplog):
|
||||
ohlcv_history.loc[df_len, "close"],
|
||||
ohlcv_history.loc[0, "date"],
|
||||
)
|
||||
with pytest.raises(StrategyError, match="enter_long/buy column not set."):
|
||||
_STRATEGY.assert_df(
|
||||
ohlcv_history.drop("enter_long", axis=1),
|
||||
len(ohlcv_history),
|
||||
ohlcv_history.loc[df_len, "close"],
|
||||
ohlcv_history.loc[0, "date"],
|
||||
)
|
||||
|
||||
_STRATEGY.disable_dataframe_checks = True
|
||||
caplog.clear()
|
||||
|
||||
+3
-3
@@ -121,7 +121,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
||||
def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(
|
||||
"freqtrade.commands.list_commands.list_available_exchanges",
|
||||
"freqtrade.exchange.list_available_exchanges",
|
||||
MagicMock(side_effect=ValueError("Oh snap!")),
|
||||
)
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
@@ -135,7 +135,7 @@ def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
||||
assert log_has("Fatal exception!", caplog)
|
||||
assert not log_has_re(r"SIGINT.*", caplog)
|
||||
mocker.patch(
|
||||
"freqtrade.commands.list_commands.list_available_exchanges",
|
||||
"freqtrade.exchange.list_available_exchanges",
|
||||
MagicMock(side_effect=KeyboardInterrupt),
|
||||
)
|
||||
with pytest.raises(SystemExit):
|
||||
@@ -147,7 +147,7 @@ def test_main_operational_exception1(mocker, default_conf, caplog) -> None:
|
||||
def test_main_ConfigurationError(mocker, default_conf, caplog) -> None:
|
||||
patch_exchange(mocker)
|
||||
mocker.patch(
|
||||
"freqtrade.commands.list_commands.list_available_exchanges",
|
||||
"freqtrade.exchange.list_available_exchanges",
|
||||
MagicMock(side_effect=ConfigurationError("Oh snap!")),
|
||||
)
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
@@ -2,20 +2,13 @@
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.commands.strategy_utils_commands import start_strategy_update
|
||||
from freqtrade.strategy.strategyupdater import StrategyUpdater
|
||||
from tests.conftest import get_args
|
||||
|
||||
|
||||
if sys.version_info < (3, 9):
|
||||
pytest.skip("StrategyUpdater is not compatible with Python 3.8", allow_module_level=True)
|
||||
|
||||
|
||||
def test_strategy_updater_start(user_dir, capsys) -> None:
|
||||
# Effective test without mocks.
|
||||
teststrats = Path(__file__).parent / "strategy/strats"
|
||||
|
||||
Reference in New Issue
Block a user