mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-02-23 21:00:56 +00:00
Merge branch 'develop' into optuna
This commit is contained in:
@@ -709,18 +709,38 @@ def test_download_and_install_ui(mocker, tmp_path):
|
||||
|
||||
def test_get_ui_download_url(mocker):
|
||||
response = MagicMock()
|
||||
response.json = MagicMock(
|
||||
side_effect=[
|
||||
[{"assets_url": "http://whatever.json", "name": "0.0.1"}],
|
||||
[{"browser_download_url": "http://download.zip"}],
|
||||
]
|
||||
)
|
||||
responses = [
|
||||
[
|
||||
{
|
||||
# Pre-release is ignored
|
||||
"assets_url": "http://whatever.json",
|
||||
"name": "0.0.2",
|
||||
"created_at": "2024-02-01T00:00:00Z",
|
||||
"prerelease": True,
|
||||
},
|
||||
{
|
||||
"assets_url": "http://whatever.json",
|
||||
"name": "0.0.1",
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"prerelease": False,
|
||||
},
|
||||
],
|
||||
[{"browser_download_url": "http://download.zip"}],
|
||||
]
|
||||
response.json = MagicMock(side_effect=responses)
|
||||
get_mock = mocker.patch("freqtrade.commands.deploy_ui.requests.get", return_value=response)
|
||||
x, last_version = get_ui_download_url()
|
||||
x, last_version = get_ui_download_url(None, False)
|
||||
assert get_mock.call_count == 2
|
||||
assert last_version == "0.0.1"
|
||||
assert x == "http://download.zip"
|
||||
|
||||
response.json = MagicMock(side_effect=responses)
|
||||
get_mock.reset_mock()
|
||||
x, last_version = get_ui_download_url(None, True)
|
||||
assert get_mock.call_count == 2
|
||||
assert last_version == "0.0.2"
|
||||
assert x == "http://download.zip"
|
||||
|
||||
|
||||
def test_get_ui_download_url_direct(mocker):
|
||||
response = MagicMock()
|
||||
@@ -729,29 +749,33 @@ def test_get_ui_download_url_direct(mocker):
|
||||
{
|
||||
"assets_url": "http://whatever.json",
|
||||
"name": "0.0.2",
|
||||
"created_at": "2024-02-01T00:00:00Z",
|
||||
"prerelease": False,
|
||||
"assets": [{"browser_download_url": "http://download22.zip"}],
|
||||
},
|
||||
{
|
||||
"assets_url": "http://whatever.json",
|
||||
"name": "0.0.1",
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"prerelease": False,
|
||||
"assets": [{"browser_download_url": "http://download1.zip"}],
|
||||
},
|
||||
]
|
||||
)
|
||||
get_mock = mocker.patch("freqtrade.commands.deploy_ui.requests.get", return_value=response)
|
||||
x, last_version = get_ui_download_url()
|
||||
x, last_version = get_ui_download_url(None, False)
|
||||
assert get_mock.call_count == 1
|
||||
assert last_version == "0.0.2"
|
||||
assert x == "http://download22.zip"
|
||||
get_mock.reset_mock()
|
||||
response.json.reset_mock()
|
||||
|
||||
x, last_version = get_ui_download_url("0.0.1")
|
||||
x, last_version = get_ui_download_url("0.0.1", False)
|
||||
assert last_version == "0.0.1"
|
||||
assert x == "http://download1.zip"
|
||||
|
||||
with pytest.raises(ValueError, match="UI-Version not found."):
|
||||
x, last_version = get_ui_download_url("0.0.3")
|
||||
x, last_version = get_ui_download_url("0.0.3", False)
|
||||
|
||||
|
||||
def test_download_data_keyboardInterrupt(mocker, markets):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import subprocess
|
||||
import subprocess # noqa: S404, RUF100
|
||||
import time
|
||||
|
||||
from tests.conftest import is_arm, is_mac
|
||||
|
||||
@@ -48,7 +48,7 @@ from tests.conftest_trades_usdt import (
|
||||
logging.getLogger("").setLevel(logging.INFO)
|
||||
|
||||
|
||||
# Do not mask numpy errors as warnings that no one read, raise the exсeption
|
||||
# Do not mask numpy errors as warnings that no one read, raise the exception
|
||||
np.seterr(all="raise")
|
||||
|
||||
CURRENT_TEST_STRATEGY = "StrategyTestV3"
|
||||
@@ -165,7 +165,7 @@ def generate_trades_history(n_rows, start_date: datetime | None = None, days=5):
|
||||
)
|
||||
df["date"] = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
|
||||
df = df.sort_values("timestamp").reset_index(drop=True)
|
||||
assert list(df.columns) == constants.DEFAULT_TRADES_COLUMNS + ["date"]
|
||||
assert list(df.columns) == [*constants.DEFAULT_TRADES_COLUMNS, "date"]
|
||||
return df
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.12508730043628782,
|
||||
"roi_p3": 0.27766427921605896,
|
||||
"stoploss": -0.2562930402099556,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 15,
|
||||
@@ -65,7 +65,7 @@ def hyperopt_test_result():
|
||||
2139: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.2562930402099556},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 2,
|
||||
"trade_count_long": 2,
|
||||
@@ -82,7 +82,7 @@ def hyperopt_test_result():
|
||||
"holding_avg": timedelta(minutes=3930.0),
|
||||
"stake_currency": "BTC",
|
||||
"strategy_name": "SampleStrategy",
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 2 trades. Avg profit -1.25%. Total profit -0.00125625 BTC ( -2.51Σ%). Avg duration 3930.0 min.", # noqa: E501
|
||||
"total_profit": -0.00125625,
|
||||
"current_epoch": 1,
|
||||
@@ -118,7 +118,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.055519840060645045,
|
||||
"roi_p3": 0.3253712811342459,
|
||||
"stoploss": -0.338070047333259,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 17,
|
||||
@@ -130,7 +130,7 @@ def hyperopt_test_result():
|
||||
"adx-enabled": True,
|
||||
"rsi-enabled": True,
|
||||
"trigger": "macd_cross_signal",
|
||||
}, # noqa: E501
|
||||
},
|
||||
"sell": {
|
||||
"sell-mfi-value": 96,
|
||||
"sell-fastd-value": 68,
|
||||
@@ -141,13 +141,13 @@ def hyperopt_test_result():
|
||||
"sell-adx-enabled": True,
|
||||
"sell-rsi-enabled": True,
|
||||
"sell-trigger": "sell-sar_reversal",
|
||||
}, # noqa: E501
|
||||
},
|
||||
"roi": {
|
||||
0: 0.4449309386008759,
|
||||
140: 0.11955965746663,
|
||||
823: 0.06403981740598495,
|
||||
1157: 0,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"stoploss": {"stoploss": -0.338070047333259},
|
||||
},
|
||||
"results_metrics": {
|
||||
@@ -164,7 +164,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.23,
|
||||
"max_drawdown_abs": -0.00125625,
|
||||
"holding_avg": timedelta(minutes=1200.0),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 1 trades. Avg profit 0.12%. Total profit 0.00006185 BTC ( 0.12Σ%). Avg duration 1200.0 min.", # noqa: E501
|
||||
"total_profit": 6.185e-05,
|
||||
"current_epoch": 2,
|
||||
@@ -200,7 +200,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.1488819964638463,
|
||||
"roi_p3": 0.4102801822104605,
|
||||
"stoploss": -0.05394588767607611,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 25,
|
||||
@@ -231,7 +231,7 @@ def hyperopt_test_result():
|
||||
1685: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.05394588767607611},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 621,
|
||||
"trade_count_long": 621,
|
||||
@@ -246,7 +246,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.25,
|
||||
"max_drawdown_abs": -272.515306,
|
||||
"holding_avg": timedelta(minutes=1691.207729468599),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 621 trades. Avg profit -0.44%. Total profit -0.13639474 BTC (-272.52Σ%). Avg duration 1691.2 min.", # noqa: E501
|
||||
"total_profit": -0.13639474,
|
||||
"current_epoch": 3,
|
||||
@@ -282,7 +282,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.14258587851894644,
|
||||
"roi_p3": 0.20671291201040828,
|
||||
"stoploss": -0.11818343570194478,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 13,
|
||||
@@ -313,7 +313,7 @@ def hyperopt_test_result():
|
||||
2293: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.11818343570194478},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 0,
|
||||
"trade_count_long": 0,
|
||||
@@ -327,13 +327,13 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.0,
|
||||
"max_drawdown_abs": 0.0,
|
||||
"holding_avg": timedelta(),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.", # noqa: E501
|
||||
"total_profit": 0,
|
||||
"current_epoch": 4,
|
||||
"is_initial_point": True,
|
||||
"is_random": False,
|
||||
"is_best": False, # noqa: E501
|
||||
"is_best": False,
|
||||
},
|
||||
{
|
||||
"loss": 0.22195522184191518,
|
||||
@@ -363,7 +363,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.08946698095898986,
|
||||
"roi_p3": 0.1454876733325284,
|
||||
"stoploss": -0.18181041180901014,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 17,
|
||||
@@ -394,7 +394,7 @@ def hyperopt_test_result():
|
||||
2314: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.18181041180901014},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 14,
|
||||
"trade_count_long": 14,
|
||||
@@ -409,7 +409,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.34,
|
||||
"max_drawdown_abs": -4.955321,
|
||||
"holding_avg": timedelta(minutes=3402.8571428571427),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 14 trades. Avg profit -0.35%. Total profit -0.00248014 BTC ( -4.96Σ%). Avg duration 3402.9 min.", # noqa: E501
|
||||
"total_profit": -0.002480140000000001,
|
||||
"current_epoch": 5,
|
||||
@@ -445,7 +445,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.11659519602202795,
|
||||
"roi_p3": 0.0953744132197762,
|
||||
"stoploss": -0.024551752215582423,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 22,
|
||||
@@ -476,7 +476,7 @@ def hyperopt_test_result():
|
||||
1091: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.024551752215582423},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 39,
|
||||
"trade_count_long": 39,
|
||||
@@ -491,7 +491,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.45,
|
||||
"max_drawdown_abs": -4.955321,
|
||||
"holding_avg": timedelta(minutes=636.9230769230769),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 39 trades. Avg profit -0.21%. Total profit -0.00417730 BTC ( -8.35Σ%). Avg duration 636.9 min.", # noqa: E501
|
||||
"total_profit": -0.0041773,
|
||||
"current_epoch": 6,
|
||||
@@ -527,7 +527,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.04984118697312542,
|
||||
"roi_p3": 0.37521058680247044,
|
||||
"stoploss": -0.14613268022709905,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 13,
|
||||
@@ -556,9 +556,9 @@ def hyperopt_test_result():
|
||||
145: 0.10853310701097472,
|
||||
765: 0.0586919200378493,
|
||||
1536: 0,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"stoploss": {"stoploss": -0.14613268022709905},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 318,
|
||||
"trade_count_long": 318,
|
||||
@@ -573,7 +573,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.50,
|
||||
"max_drawdown_abs": -200.955321,
|
||||
"holding_avg": timedelta(minutes=3140.377358490566),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 318 trades. Avg profit -0.40%. Total profit -0.06339929 BTC (-126.67Σ%). Avg duration 3140.4 min.", # noqa: E501
|
||||
"total_profit": -0.06339929,
|
||||
"current_epoch": 7,
|
||||
@@ -582,7 +582,7 @@ def hyperopt_test_result():
|
||||
"is_best": False,
|
||||
},
|
||||
{
|
||||
"loss": 20.0, # noqa: E501
|
||||
"loss": 20.0,
|
||||
"params_dict": {
|
||||
"mfi-value": 24,
|
||||
"fastd-value": 43,
|
||||
@@ -609,7 +609,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.0606240398618907,
|
||||
"roi_p3": 0.1729012220156157,
|
||||
"stoploss": -0.1588514289110401,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 24,
|
||||
@@ -640,7 +640,7 @@ def hyperopt_test_result():
|
||||
1813: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.1588514289110401},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 1,
|
||||
"trade_count_long": 1,
|
||||
@@ -655,7 +655,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.0,
|
||||
"max_drawdown_abs": 0.52,
|
||||
"holding_avg": timedelta(minutes=5340.0),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 1 trades. Avg profit 0.00%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration 5340.0 min.", # noqa: E501
|
||||
"total_profit": 0.0,
|
||||
"current_epoch": 8,
|
||||
@@ -691,7 +691,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.10335480573205287,
|
||||
"roi_p3": 0.10322347377503042,
|
||||
"stoploss": -0.2780610808108503,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 22,
|
||||
@@ -722,7 +722,7 @@ def hyperopt_test_result():
|
||||
2018: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.2780610808108503},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 229,
|
||||
"trade_count_long": 229,
|
||||
@@ -737,16 +737,16 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.41,
|
||||
"max_drawdown_abs": -150.955321,
|
||||
"holding_avg": timedelta(minutes=6505.676855895196),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 229 trades. Avg profit -0.38%. Total profit -0.04405007 BTC ( -88.01Σ%). Avg duration 6505.7 min.", # noqa: E501
|
||||
"total_profit": -0.044050070000000004, # noqa: E501
|
||||
"total_profit": -0.044050070000000004,
|
||||
"current_epoch": 9,
|
||||
"is_initial_point": True,
|
||||
"is_random": False,
|
||||
"is_best": False,
|
||||
},
|
||||
{
|
||||
"loss": -0.2604606005845212, # noqa: E501
|
||||
"loss": -0.2604606005845212,
|
||||
"params_dict": {
|
||||
"mfi-value": 23,
|
||||
"fastd-value": 24,
|
||||
@@ -773,7 +773,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.09623192684243963,
|
||||
"roi_p3": 0.04428219070850663,
|
||||
"stoploss": -0.16992287161634415,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 23,
|
||||
@@ -804,7 +804,7 @@ def hyperopt_test_result():
|
||||
1471: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.16992287161634415},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 4,
|
||||
"trade_count_long": 4,
|
||||
@@ -819,7 +819,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.13,
|
||||
"max_drawdown_abs": -4.955321,
|
||||
"holding_avg": timedelta(minutes=2850.0),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 4 trades. Avg profit 0.11%. Total profit 0.00021629 BTC ( 0.43Σ%). Avg duration 2850.0 min.", # noqa: E501
|
||||
"total_profit": 0.00021629,
|
||||
"current_epoch": 10,
|
||||
@@ -828,7 +828,7 @@ def hyperopt_test_result():
|
||||
"is_best": True,
|
||||
},
|
||||
{
|
||||
"loss": 4.876465945994304, # noqa: E501
|
||||
"loss": 4.876465945994304,
|
||||
"params_dict": {
|
||||
"mfi-value": 20,
|
||||
"fastd-value": 32,
|
||||
@@ -855,7 +855,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.1352282078262871,
|
||||
"roi_p3": 0.1913307406325751,
|
||||
"stoploss": -0.25728526022513887,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 20,
|
||||
@@ -886,7 +886,7 @@ def hyperopt_test_result():
|
||||
1466: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.25728526022513887},
|
||||
}, # noqa: E501
|
||||
},
|
||||
# New Hyperopt mode!
|
||||
"results_metrics": {
|
||||
"total_trades": 117,
|
||||
@@ -902,7 +902,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.52,
|
||||
"max_drawdown_abs": -224.955321,
|
||||
"holding_avg": timedelta(minutes=4282.5641025641025),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 117 trades. Avg profit -1.27%. Total profit -0.07436117 BTC (-148.57Σ%). Avg duration 4282.6 min.", # noqa: E501
|
||||
"total_profit": -0.07436117,
|
||||
"current_epoch": 11,
|
||||
@@ -938,7 +938,7 @@ def hyperopt_test_result():
|
||||
"roi_p2": 0.12473718444931989,
|
||||
"roi_p3": 0.2896360635226823,
|
||||
"stoploss": -0.30889015124682806,
|
||||
}, # noqa: E501
|
||||
},
|
||||
"params_details": {
|
||||
"buy": {
|
||||
"mfi-value": 10,
|
||||
@@ -969,7 +969,7 @@ def hyperopt_test_result():
|
||||
2145: 0,
|
||||
},
|
||||
"stoploss": {"stoploss": -0.30889015124682806},
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_metrics": {
|
||||
"total_trades": 0,
|
||||
"trade_count_long": 0,
|
||||
@@ -984,7 +984,7 @@ def hyperopt_test_result():
|
||||
"max_drawdown_account": 0.0,
|
||||
"max_drawdown_abs": 0.0,
|
||||
"holding_avg": timedelta(),
|
||||
}, # noqa: E501
|
||||
},
|
||||
"results_explanation": " 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.", # noqa: E501
|
||||
"total_profit": 0,
|
||||
"current_epoch": 12,
|
||||
|
||||
@@ -56,7 +56,7 @@ def test_get_latest_backtest_filename(testdatadir, mocker):
|
||||
res = get_latest_backtest_filename(str(testdir_bt))
|
||||
assert res == "backtest-result.json"
|
||||
|
||||
mocker.patch("freqtrade.data.btanalysis.json_load", return_value={})
|
||||
mocker.patch("freqtrade.data.btanalysis.bt_fileutils.json_load", return_value={})
|
||||
|
||||
with pytest.raises(ValueError, match=r"Invalid '.last_result.json' format."):
|
||||
get_latest_backtest_filename(testdir_bt)
|
||||
@@ -84,8 +84,8 @@ def test_load_backtest_metadata(mocker, testdatadir):
|
||||
res = load_backtest_metadata(testdatadir / "nonexistent.file.json")
|
||||
assert res == {}
|
||||
|
||||
mocker.patch("freqtrade.data.btanalysis.get_backtest_metadata_filename")
|
||||
mocker.patch("freqtrade.data.btanalysis.json_load", side_effect=Exception())
|
||||
mocker.patch("freqtrade.data.btanalysis.bt_fileutils.get_backtest_metadata_filename")
|
||||
mocker.patch("freqtrade.data.btanalysis.bt_fileutils.json_load", side_effect=Exception())
|
||||
with pytest.raises(
|
||||
OperationalException, match=r"Unexpected error.*loading backtest metadata\."
|
||||
):
|
||||
@@ -94,7 +94,7 @@ def test_load_backtest_metadata(mocker, testdatadir):
|
||||
|
||||
def test_load_backtest_data_old_format(testdatadir, mocker):
|
||||
filename = testdatadir / "backtest-result_test222.json"
|
||||
mocker.patch("freqtrade.data.btanalysis.load_backtest_stats", return_value=[])
|
||||
mocker.patch("freqtrade.data.btanalysis.bt_fileutils.load_backtest_stats", return_value=[])
|
||||
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
@@ -149,7 +149,7 @@ def test_load_backtest_data_multi(testdatadir):
|
||||
def test_load_trades_from_db(default_conf, fee, is_short, mocker):
|
||||
create_mock_trades(fee, is_short)
|
||||
# remove init so it does not init again
|
||||
init_mock = mocker.patch("freqtrade.data.btanalysis.init_db", MagicMock())
|
||||
init_mock = mocker.patch("freqtrade.data.btanalysis.bt_fileutils.init_db", MagicMock())
|
||||
|
||||
trades = load_trades_from_db(db_url=default_conf["db_url"])
|
||||
assert init_mock.call_count == 1
|
||||
@@ -221,8 +221,10 @@ def test_analyze_trade_parallelism(testdatadir):
|
||||
|
||||
|
||||
def test_load_trades(default_conf, mocker):
|
||||
db_mock = mocker.patch("freqtrade.data.btanalysis.load_trades_from_db", MagicMock())
|
||||
bt_mock = mocker.patch("freqtrade.data.btanalysis.load_backtest_data", MagicMock())
|
||||
db_mock = mocker.patch(
|
||||
"freqtrade.data.btanalysis.bt_fileutils.load_trades_from_db", MagicMock()
|
||||
)
|
||||
bt_mock = mocker.patch("freqtrade.data.btanalysis.bt_fileutils.load_backtest_data", MagicMock())
|
||||
|
||||
load_trades(
|
||||
"DB",
|
||||
@@ -268,6 +270,14 @@ def test_calculate_market_change(testdatadir):
|
||||
assert isinstance(result, float)
|
||||
assert pytest.approx(result) == 0.01100002
|
||||
|
||||
result = calculate_market_change(data, min_date=dt_utc(2018, 1, 20))
|
||||
assert isinstance(result, float)
|
||||
assert pytest.approx(result) == 0.0375149
|
||||
|
||||
# Move min-date after the last date
|
||||
result = calculate_market_change(data, min_date=dt_utc(2018, 2, 20))
|
||||
assert pytest.approx(result) == 0.0
|
||||
|
||||
|
||||
def test_combine_dataframes_with_mean(testdatadir):
|
||||
pairs = ["ETH/BTC", "ADA/BTC"]
|
||||
@@ -562,14 +572,15 @@ def test_calculate_max_drawdown2():
|
||||
assert pytest.approx(drawdown.relative_account_drawdown) == 0.32129575
|
||||
|
||||
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")
|
||||
# No losing trade ...
|
||||
drawdown = calculate_max_drawdown(df, date_col="open_date", value_col="profit")
|
||||
assert drawdown.drawdown_abs == 0.0
|
||||
|
||||
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")
|
||||
assert drawdown.drawdown_abs == 0.043965
|
||||
assert drawdown.drawdown_abs == 0.055545
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -139,7 +139,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
|
||||
# test group 0 and indicator list
|
||||
args = get_args(
|
||||
base_args + ["--analysis-groups", "0", "--indicator-list", "close", "rsi", "profit_abs"]
|
||||
[*base_args, "--analysis-groups", "0", "--indicator-list", "close", "rsi", "profit_abs"]
|
||||
)
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
@@ -172,7 +172,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
assert "profit_abs" in captured.out
|
||||
|
||||
# test group 1
|
||||
args = get_args(base_args + ["--analysis-groups", "1"])
|
||||
args = get_args([*base_args, "--analysis-groups", "1"])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert "enter_tag_long_a" in captured.out
|
||||
@@ -185,7 +185,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
assert "0" in captured.out
|
||||
|
||||
# test group 2
|
||||
args = get_args(base_args + ["--analysis-groups", "2"])
|
||||
args = get_args([*base_args, "--analysis-groups", "2"])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert "enter_tag_long_a" in captured.out
|
||||
@@ -200,7 +200,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
assert "2.5" in captured.out
|
||||
|
||||
# test group 3
|
||||
args = get_args(base_args + ["--analysis-groups", "3"])
|
||||
args = get_args([*base_args, "--analysis-groups", "3"])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert "LTC/BTC" in captured.out
|
||||
@@ -215,7 +215,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
assert "2" in captured.out
|
||||
|
||||
# test group 4
|
||||
args = get_args(base_args + ["--analysis-groups", "4"])
|
||||
args = get_args([*base_args, "--analysis-groups", "4"])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert "LTC/BTC" in captured.out
|
||||
@@ -235,7 +235,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
assert "2.5" in captured.out
|
||||
|
||||
# test group 5
|
||||
args = get_args(base_args + ["--analysis-groups", "5"])
|
||||
args = get_args([*base_args, "--analysis-groups", "5"])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert "exit_signal" in captured.out
|
||||
@@ -245,7 +245,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
|
||||
# test date filtering
|
||||
args = get_args(
|
||||
base_args + ["--analysis-groups", "0", "1", "2", "--timerange", "20180129-20180130"]
|
||||
[*base_args, "--analysis-groups", "0", "1", "2", "--timerange", "20180129-20180130"]
|
||||
)
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
@@ -253,7 +253,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||
assert "enter_tag_long_b" not in captured.out
|
||||
|
||||
# Due to the backtest mock, there's no rejected signals generated.
|
||||
args = get_args(base_args + ["--rejected-signals"])
|
||||
args = get_args([*base_args, "--rejected-signals"])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert "no rejected signals" in captured.out
|
||||
@@ -379,8 +379,8 @@ def test_backtest_analysis_with_invalid_config(
|
||||
|
||||
# test with both entry and exit only arguments
|
||||
args = get_args(
|
||||
base_args
|
||||
+ [
|
||||
[
|
||||
*base_args,
|
||||
"--analysis-groups",
|
||||
"0",
|
||||
"--indicator-list",
|
||||
@@ -518,8 +518,8 @@ def test_backtest_analysis_on_entry_and_rejected_signals_only_entry_signals(
|
||||
|
||||
# test group 0 and indicator list
|
||||
args = get_args(
|
||||
base_args
|
||||
+ [
|
||||
[
|
||||
*base_args,
|
||||
"--analysis-groups",
|
||||
"0",
|
||||
"--indicator-list",
|
||||
|
||||
@@ -299,7 +299,7 @@ def test_liquidation_price_binance(
|
||||
|
||||
def get_maint_ratio(pair_, stake_amount):
|
||||
if pair_ != pair:
|
||||
oc = [c for c in open_trades if c["pair"] == pair_][0]
|
||||
oc = next(c for c in open_trades if c["pair"] == pair_)
|
||||
return oc["mm_ratio"], oc["maintenance_amt"]
|
||||
return mm_ratio, maintenance_amt
|
||||
|
||||
|
||||
@@ -2838,6 +2838,11 @@ def test_get_next_limit_in_list():
|
||||
assert Exchange.get_next_limit_in_list(21, None) == 21
|
||||
assert Exchange.get_next_limit_in_list(100, None) == 100
|
||||
assert Exchange.get_next_limit_in_list(1000, None) == 1000
|
||||
# With upper limit
|
||||
assert Exchange.get_next_limit_in_list(1000, None, upper_limit=None) == 1000
|
||||
assert Exchange.get_next_limit_in_list(1000, None, upper_limit=500) == 500
|
||||
# with upper limit and range, limit_range wins
|
||||
assert Exchange.get_next_limit_in_list(1000, limit_range, upper_limit=500) == 1000
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
@@ -5594,11 +5599,13 @@ def test_liquidation_price_is_none(
|
||||
def test_get_max_pair_stake_amount(
|
||||
mocker,
|
||||
default_conf,
|
||||
leverage_tiers,
|
||||
):
|
||||
api_mock = MagicMock()
|
||||
default_conf["margin_mode"] = "isolated"
|
||||
default_conf["trading_mode"] = "futures"
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange._leverage_tiers = leverage_tiers
|
||||
markets = {
|
||||
"XRP/USDT:USDT": {
|
||||
"limits": {
|
||||
@@ -5662,11 +5669,23 @@ def test_get_max_pair_stake_amount(
|
||||
"contractSize": 0.01,
|
||||
"spot": False,
|
||||
},
|
||||
"ZEC/USDT:USDT": {
|
||||
"limits": {
|
||||
"amount": {"min": 0.001, "max": None},
|
||||
"cost": {"min": 5, "max": None},
|
||||
},
|
||||
"contractSize": 1,
|
||||
"spot": False,
|
||||
},
|
||||
}
|
||||
|
||||
mocker.patch(f"{EXMS}.markets", markets)
|
||||
assert exchange.get_max_pair_stake_amount("XRP/USDT:USDT", 2.0) == 20000
|
||||
assert exchange.get_max_pair_stake_amount("XRP/USDT:USDT", 2.0, 5) == 4000
|
||||
# limit leverage tiers
|
||||
assert exchange.get_max_pair_stake_amount("ZEC/USDT:USDT", 2.0, 5) == 100_000
|
||||
assert exchange.get_max_pair_stake_amount("ZEC/USDT:USDT", 2.0, 50) == 1000
|
||||
|
||||
assert exchange.get_max_pair_stake_amount("LTC/USDT:USDT", 2.0) == float("inf")
|
||||
assert exchange.get_max_pair_stake_amount("ETH/USDT:USDT", 2.0) == 200
|
||||
assert exchange.get_max_pair_stake_amount("DOGE/USDT:USDT", 2.0) == 500
|
||||
@@ -5897,8 +5916,8 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
|
||||
assert exchange.get_max_leverage("XRP/USDT:USDT", 1.0) == 20.0
|
||||
assert exchange.get_max_leverage("BNB/USDT:USDT", 100.0) == 75.0
|
||||
assert exchange.get_max_leverage("BTC/USDT:USDT", 170.30) == 125.0
|
||||
assert pytest.approx(exchange.get_max_leverage("XRP/USDT:USDT", 99999.9)) == 5.000005
|
||||
assert pytest.approx(exchange.get_max_leverage("BNB/USDT:USDT", 1500)) == 33.333333333333333
|
||||
assert pytest.approx(exchange.get_max_leverage("XRP/USDT:USDT", 99999.9)) == 5
|
||||
assert pytest.approx(exchange.get_max_leverage("BNB/USDT:USDT", 1500)) == 25
|
||||
assert exchange.get_max_leverage("BTC/USDT:USDT", 300000000) == 2.0
|
||||
assert exchange.get_max_leverage("BTC/USDT:USDT", 600000000) == 1.0 # Last tier
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ def test_get_balances_prod_kraken(default_conf, mocker):
|
||||
"4TH": balance_item.copy(),
|
||||
"EUR": balance_item.copy(),
|
||||
"BTC": {"free": 0.0, "total": 0.0, "used": 0.0},
|
||||
"XBT.F": balance_item.copy(),
|
||||
"BTC.F": balance_item.copy(),
|
||||
"timestamp": 123123,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -156,7 +156,7 @@ EXCHANGES = {
|
||||
"ADA.F": {"free": 2.0, "total": 2.0, "used": 0.0},
|
||||
"BTC": {"free": 0.0006, "total": 0.0006, "used": 0.0},
|
||||
# XBT.F should be mapped to BTC.F
|
||||
"XBT.F": {"free": 0.001, "total": 0.001, "used": 0.0},
|
||||
"BTC.F": {"free": 0.001, "total": 0.001, "used": 0.0},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import time
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pytest
|
||||
import time_machine
|
||||
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
@@ -38,6 +39,25 @@ def test_worker_running(mocker, default_conf, caplog) -> None:
|
||||
assert isinstance(worker.freqtrade.strategy.dp, DataProvider)
|
||||
|
||||
|
||||
def test_worker_paused(mocker, default_conf, caplog) -> None:
|
||||
mock_throttle = MagicMock()
|
||||
mocker.patch("freqtrade.worker.Worker._throttle", mock_throttle)
|
||||
mocker.patch("freqtrade.persistence.Trade.stoploss_reinitialization", MagicMock())
|
||||
|
||||
worker = get_patched_worker(mocker, default_conf)
|
||||
|
||||
worker.freqtrade.state = State.PAUSED
|
||||
state = worker._worker(old_state=State.RUNNING)
|
||||
|
||||
assert state is State.PAUSED
|
||||
assert log_has("Changing state from RUNNING to: PAUSED", caplog)
|
||||
assert mock_throttle.call_count == 1
|
||||
# Check strategy is loaded, and received a dataprovider object
|
||||
assert worker.freqtrade.strategy
|
||||
assert worker.freqtrade.strategy.dp
|
||||
assert isinstance(worker.freqtrade.strategy.dp, DataProvider)
|
||||
|
||||
|
||||
def test_worker_stopped(mocker, default_conf, caplog) -> None:
|
||||
mock_throttle = MagicMock()
|
||||
mocker.patch("freqtrade.worker.Worker._throttle", mock_throttle)
|
||||
@@ -50,6 +70,54 @@ def test_worker_stopped(mocker, default_conf, caplog) -> None:
|
||||
assert mock_throttle.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"old_state,target_state,startup_call,log_fragment",
|
||||
[
|
||||
(State.STOPPED, State.PAUSED, True, "Changing state from STOPPED to: PAUSED"),
|
||||
(State.RUNNING, State.PAUSED, False, "Changing state from RUNNING to: PAUSED"),
|
||||
(State.PAUSED, State.RUNNING, False, "Changing state from PAUSED to: RUNNING"),
|
||||
(State.PAUSED, State.STOPPED, False, "Changing state from PAUSED to: STOPPED"),
|
||||
(State.RELOAD_CONFIG, State.RUNNING, True, "Changing state from RELOAD_CONFIG to: RUNNING"),
|
||||
(
|
||||
State.RELOAD_CONFIG,
|
||||
State.STOPPED,
|
||||
False,
|
||||
"Changing state from RELOAD_CONFIG to: STOPPED",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_worker_lifecycle(
|
||||
mocker,
|
||||
default_conf,
|
||||
caplog,
|
||||
old_state,
|
||||
target_state,
|
||||
startup_call,
|
||||
log_fragment,
|
||||
):
|
||||
mock_throttle = mocker.MagicMock()
|
||||
mocker.patch("freqtrade.worker.Worker._throttle", mock_throttle)
|
||||
mocker.patch("freqtrade.persistence.Trade.stoploss_reinitialization")
|
||||
startup = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.startup")
|
||||
|
||||
worker = get_patched_worker(mocker, default_conf)
|
||||
worker.freqtrade.state = target_state
|
||||
|
||||
new_state = worker._worker(old_state=old_state)
|
||||
|
||||
assert new_state is target_state
|
||||
assert log_has(log_fragment, caplog)
|
||||
assert mock_throttle.call_count == 1
|
||||
assert startup.call_count == (1 if startup_call else 0)
|
||||
|
||||
# For any state where the strategy should be initialized
|
||||
if target_state in (State.RUNNING, State.PAUSED):
|
||||
assert worker.freqtrade.strategy
|
||||
assert isinstance(worker.freqtrade.strategy.dp, DataProvider)
|
||||
else:
|
||||
assert new_state is State.STOPPED
|
||||
|
||||
|
||||
def test_throttle(mocker, default_conf, caplog) -> None:
|
||||
def throttled_func():
|
||||
return 42
|
||||
|
||||
@@ -69,8 +69,8 @@ def _build_backtest_dataframe(data):
|
||||
]
|
||||
if len(data[0]) == 8:
|
||||
# No short columns
|
||||
data = [d + [0, 0] for d in data]
|
||||
columns = columns + ["enter_tag"] if len(data[0]) == 11 else columns
|
||||
data = [[*d, 0, 0] for d in data]
|
||||
columns = [*columns, "enter_tag"] if len(data[0]) == 11 else columns
|
||||
|
||||
frame = DataFrame.from_records(data, columns=columns)
|
||||
frame["date"] = frame["date"].apply(_get_frame_time_from_offset)
|
||||
|
||||
@@ -102,7 +102,7 @@ tc3 = BTContainer(
|
||||
)
|
||||
|
||||
# Test 4: Minus 3% / recovery +15%
|
||||
# Candle Data for test 3 – Candle drops 3% Closed 15% up
|
||||
# Candle Data for test 3 - Candle drops 3% Closed 15% up
|
||||
# Test with Stop-loss at 2% ROI 6%
|
||||
# Stop-Loss Triggered 2% Loss
|
||||
tc4 = BTContainer(
|
||||
|
||||
@@ -828,6 +828,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None:
|
||||
},
|
||||
],
|
||||
],
|
||||
"funding_fees": [0.0, 0.0],
|
||||
}
|
||||
)
|
||||
pd.testing.assert_frame_equal(results, expected)
|
||||
@@ -991,7 +992,7 @@ def test_backtest_one_detail_futures(
|
||||
timerange=timerange,
|
||||
candle_type=CandleType.FUTURES,
|
||||
)
|
||||
backtesting.load_bt_data_detail()
|
||||
backtesting._load_bt_data_detail()
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
|
||||
@@ -1119,7 +1120,7 @@ def test_backtest_one_detail_futures_funding_fees(
|
||||
timerange=timerange,
|
||||
candle_type=CandleType.FUTURES,
|
||||
)
|
||||
backtesting.load_bt_data_detail()
|
||||
backtesting._load_bt_data_detail()
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
|
||||
@@ -2576,7 +2577,7 @@ def test_backtest_start_multi_strat_caching(
|
||||
],
|
||||
)
|
||||
mocker.patch.multiple(
|
||||
"freqtrade.data.btanalysis",
|
||||
"freqtrade.data.btanalysis.bt_fileutils",
|
||||
load_backtest_metadata=load_backtest_metadata,
|
||||
load_backtest_stats=load_backtest_stats,
|
||||
)
|
||||
|
||||
@@ -80,6 +80,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
|
||||
"is_short": [False, False],
|
||||
"open_timestamp": [1517251200000, 1517283000000],
|
||||
"close_timestamp": [1517263200000, 1517285400000],
|
||||
"funding_fees": [0.0, 0.0],
|
||||
}
|
||||
)
|
||||
results_no = results.drop(columns=["orders"])
|
||||
|
||||
@@ -685,7 +685,7 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
||||
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi"'
|
||||
':{},"stoploss":null,"trailing_stop":null,"max_open_trades":null}'
|
||||
)
|
||||
assert result_str in out # noqa: E501
|
||||
assert result_str in out
|
||||
# Should be called for historical candle data
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
@@ -742,7 +742,7 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
||||
assert (
|
||||
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}'
|
||||
in out
|
||||
) # noqa: E501
|
||||
)
|
||||
# Should be called for historical candle data
|
||||
assert dumper.call_count == 1
|
||||
assert dumper2.call_count == 1
|
||||
|
||||
@@ -153,6 +153,7 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) ->
|
||||
"SharpeHyperOptLossDaily",
|
||||
"MaxDrawDownHyperOptLoss",
|
||||
"MaxDrawDownRelativeHyperOptLoss",
|
||||
"MaxDrawDownPerPairHyperOptLoss",
|
||||
"CalmarHyperOptLoss",
|
||||
"ProfitDrawDownHyperOptLoss",
|
||||
"MultiMetricHyperOptLoss",
|
||||
@@ -165,6 +166,34 @@ def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunct
|
||||
results_under = hyperopt_results.copy()
|
||||
results_under["profit_abs"] = hyperopt_results["profit_abs"] / 2 - 0.2
|
||||
results_under["profit_ratio"] = hyperopt_results["profit_ratio"] / 2
|
||||
pair_results = [
|
||||
{
|
||||
"key": "ETH/USDT",
|
||||
"max_drawdown_abs": 50.0,
|
||||
"profit_total_abs": 100.0,
|
||||
},
|
||||
{
|
||||
"key": "BTC/USDT",
|
||||
"max_drawdown_abs": 50.0,
|
||||
"profit_total_abs": 100.0,
|
||||
},
|
||||
]
|
||||
pair_results_over = [
|
||||
{
|
||||
**p,
|
||||
"max_drawdown_abs": p["max_drawdown_abs"] * 0.5,
|
||||
"profit_total_abs": p["profit_total_abs"] * 2,
|
||||
}
|
||||
for p in pair_results
|
||||
]
|
||||
pair_results_under = [
|
||||
{
|
||||
**p,
|
||||
"max_drawdown_abs": p["max_drawdown_abs"] * 2,
|
||||
"profit_total_abs": p["profit_total_abs"] * 0.5,
|
||||
}
|
||||
for p in pair_results
|
||||
]
|
||||
|
||||
default_conf.update({"hyperopt_loss": lossfunction})
|
||||
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
||||
@@ -175,7 +204,10 @@ def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunct
|
||||
max_date=datetime(2019, 5, 1),
|
||||
config=default_conf,
|
||||
processed=None,
|
||||
backtest_stats={"profit_total": hyperopt_results["profit_abs"].sum()},
|
||||
backtest_stats={
|
||||
"profit_total": hyperopt_results["profit_abs"].sum(),
|
||||
"results_per_pair": pair_results,
|
||||
},
|
||||
starting_balance=default_conf["dry_run_wallet"],
|
||||
)
|
||||
over = hl.hyperopt_loss_function(
|
||||
@@ -185,7 +217,10 @@ def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunct
|
||||
max_date=datetime(2019, 5, 1),
|
||||
config=default_conf,
|
||||
processed=None,
|
||||
backtest_stats={"profit_total": results_over["profit_abs"].sum()},
|
||||
backtest_stats={
|
||||
"profit_total": results_over["profit_abs"].sum(),
|
||||
"results_per_pair": pair_results_over,
|
||||
},
|
||||
starting_balance=default_conf["dry_run_wallet"],
|
||||
)
|
||||
under = hl.hyperopt_loss_function(
|
||||
@@ -195,7 +230,10 @@ def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunct
|
||||
max_date=datetime(2019, 5, 1),
|
||||
config=default_conf,
|
||||
processed=None,
|
||||
backtest_stats={"profit_total": results_under["profit_abs"].sum()},
|
||||
backtest_stats={
|
||||
"profit_total": results_under["profit_abs"].sum(),
|
||||
"results_per_pair": pair_results_under,
|
||||
},
|
||||
starting_balance=default_conf["dry_run_wallet"],
|
||||
)
|
||||
assert over < correct
|
||||
|
||||
@@ -68,11 +68,21 @@ def test_text_table_bt_results(capsys):
|
||||
"profit_ratio": [0.1, 0.2, -0.05],
|
||||
"profit_abs": [0.2, 0.4, -0.1],
|
||||
"trade_duration": [10, 30, 20],
|
||||
"close_date": [
|
||||
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||
dt_utc(2017, 11, 14, 22, 43, 00),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
pair_results = generate_pair_metrics(
|
||||
["ETH/BTC"], stake_currency="BTC", starting_balance=4, results=results
|
||||
["ETH/BTC"],
|
||||
stake_currency="BTC",
|
||||
starting_balance=4,
|
||||
results=results,
|
||||
min_date=dt_from_ts(1510688220),
|
||||
max_date=dt_from_ts(1510700340),
|
||||
)
|
||||
text_table_bt_results(pair_results, stake_currency="BTC", title="title")
|
||||
text = capsys.readouterr().out
|
||||
@@ -420,6 +430,10 @@ def test_generate_pair_metrics():
|
||||
"profit_ratio": [0.1, 0.2],
|
||||
"profit_abs": [0.2, 0.4],
|
||||
"trade_duration": [10, 30],
|
||||
"close_date": [
|
||||
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||
],
|
||||
"wins": [2, 0],
|
||||
"draws": [0, 0],
|
||||
"losses": [0, 0],
|
||||
@@ -427,7 +441,12 @@ def test_generate_pair_metrics():
|
||||
)
|
||||
|
||||
pair_results = generate_pair_metrics(
|
||||
["ETH/BTC"], stake_currency="BTC", starting_balance=2, results=results
|
||||
["ETH/BTC"],
|
||||
stake_currency="BTC",
|
||||
starting_balance=2,
|
||||
results=results,
|
||||
min_date=dt_from_ts(1510688220),
|
||||
max_date=dt_from_ts(1510700340),
|
||||
)
|
||||
assert isinstance(pair_results, list)
|
||||
assert len(pair_results) == 2
|
||||
@@ -512,6 +531,11 @@ def test_text_table_exit_reason(capsys):
|
||||
"profit_ratio": [0.1, 0.2, -0.1],
|
||||
"profit_abs": [0.2, 0.4, -0.2],
|
||||
"trade_duration": [10, 30, 10],
|
||||
"close_date": [
|
||||
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||
dt_utc(2017, 11, 14, 22, 43, 00),
|
||||
],
|
||||
"wins": [2, 0, 0],
|
||||
"draws": [0, 0, 0],
|
||||
"losses": [0, 0, 1],
|
||||
@@ -520,7 +544,12 @@ def test_text_table_exit_reason(capsys):
|
||||
)
|
||||
|
||||
exit_reason_stats = generate_tag_metrics(
|
||||
"exit_reason", starting_balance=22, results=results, skip_nan=False
|
||||
"exit_reason",
|
||||
starting_balance=22,
|
||||
results=results,
|
||||
min_date=dt_from_ts(1510688220),
|
||||
max_date=dt_from_ts(1510700340),
|
||||
skip_nan=False,
|
||||
)
|
||||
text_table_tags("exit_tag", exit_reason_stats, "BTC")
|
||||
text = capsys.readouterr().out
|
||||
@@ -550,6 +579,11 @@ def test_generate_sell_reason_stats():
|
||||
"profit_ratio": [0.1, 0.2, -0.1],
|
||||
"profit_abs": [0.2, 0.4, -0.2],
|
||||
"trade_duration": [10, 30, 10],
|
||||
"close_date": [
|
||||
dt_utc(2017, 11, 14, 21, 35, 00),
|
||||
dt_utc(2017, 11, 14, 22, 10, 00),
|
||||
dt_utc(2017, 11, 14, 22, 43, 00),
|
||||
],
|
||||
"wins": [2, 0, 0],
|
||||
"draws": [0, 0, 0],
|
||||
"losses": [0, 0, 1],
|
||||
@@ -558,7 +592,12 @@ def test_generate_sell_reason_stats():
|
||||
)
|
||||
|
||||
exit_reason_stats = generate_tag_metrics(
|
||||
"exit_reason", starting_balance=22, results=results, skip_nan=False
|
||||
"exit_reason",
|
||||
starting_balance=22,
|
||||
results=results,
|
||||
min_date=dt_from_ts(1510688220),
|
||||
max_date=dt_from_ts(1510700340),
|
||||
skip_nan=False,
|
||||
)
|
||||
roi_result = exit_reason_stats[0]
|
||||
assert roi_result["key"] == "roi"
|
||||
|
||||
@@ -880,68 +880,68 @@ def test_calc_close_trade_price(
|
||||
"exchange,is_short,lev,close_rate,fee_close,profit,profit_ratio,trading_mode,funding_fees",
|
||||
[
|
||||
("binance", False, 1, 2.1, 0.0025, 2.6925, 0.044763092, spot, 0),
|
||||
("binance", False, 3, 2.1, 0.0025, 2.69166667, 0.134247714, margin, 0),
|
||||
("binance", False, 3, 2.1, 0.0025, 8.075, 0.134247714, margin, 0),
|
||||
("binance", True, 1, 2.1, 0.0025, -3.3088157, -0.055285142, margin, 0),
|
||||
("binance", True, 3, 2.1, 0.0025, -3.3088157, -0.16585542, margin, 0),
|
||||
("binance", True, 3, 2.1, 0.0025, -9.92644709, -0.16585542, margin, 0),
|
||||
("binance", False, 1, 1.9, 0.0025, -3.2925, -0.054738154, margin, 0),
|
||||
("binance", False, 3, 1.9, 0.0025, -3.29333333, -0.164256026, margin, 0),
|
||||
("binance", False, 3, 1.9, 0.0025, -9.88, -0.164256026, margin, 0),
|
||||
("binance", True, 1, 1.9, 0.0025, 2.70630953, 0.0452182043, margin, 0),
|
||||
("binance", True, 3, 1.9, 0.0025, 2.70630953, 0.135654613, margin, 0),
|
||||
("binance", True, 3, 1.9, 0.0025, 8.11892859, 0.135654613, margin, 0),
|
||||
("binance", False, 1, 2.2, 0.0025, 5.685, 0.09451371, margin, 0),
|
||||
("binance", False, 3, 2.2, 0.0025, 5.68416667, 0.28349958, margin, 0),
|
||||
("binance", False, 3, 2.2, 0.0025, 17.0525, 0.28349958, margin, 0),
|
||||
("binance", True, 1, 2.2, 0.0025, -6.3163784, -0.10553681, margin, 0),
|
||||
("binance", True, 3, 2.2, 0.0025, -6.3163784, -0.31661044, margin, 0),
|
||||
("binance", True, 3, 2.2, 0.0025, -18.94913, -0.31661044, margin, 0),
|
||||
# Kraken
|
||||
("kraken", False, 1, 2.1, 0.0025, 2.6925, 0.044763092, spot, 0),
|
||||
("kraken", False, 3, 2.1, 0.0025, 2.6525, 0.132294264, margin, 0),
|
||||
("kraken", False, 3, 2.1, 0.0025, 7.9575, 0.132294264, margin, 0),
|
||||
("kraken", True, 1, 2.1, 0.0025, -3.3706575, -0.056318421, margin, 0),
|
||||
("kraken", True, 3, 2.1, 0.0025, -3.3706575, -0.168955263, margin, 0),
|
||||
("kraken", True, 3, 2.1, 0.0025, -10.1119725, -0.168955263, margin, 0),
|
||||
("kraken", False, 1, 1.9, 0.0025, -3.2925, -0.054738154, margin, 0),
|
||||
("kraken", False, 3, 1.9, 0.0025, -3.3325, -0.166209476, margin, 0),
|
||||
("kraken", False, 3, 1.9, 0.0025, -9.9975, -0.166209476, margin, 0),
|
||||
("kraken", True, 1, 1.9, 0.0025, 2.6503575, 0.044283333, margin, 0),
|
||||
("kraken", True, 3, 1.9, 0.0025, 2.6503575, 0.132850000, margin, 0),
|
||||
("kraken", True, 3, 1.9, 0.0025, 7.9510725, 0.132850000, margin, 0),
|
||||
("kraken", False, 1, 2.2, 0.0025, 5.685, 0.09451371, margin, 0),
|
||||
("kraken", False, 3, 2.2, 0.0025, 5.645, 0.28154613, margin, 0),
|
||||
("kraken", False, 3, 2.2, 0.0025, 16.935, 0.28154613, margin, 0),
|
||||
("kraken", True, 1, 2.2, 0.0025, -6.381165, -0.1066192, margin, 0),
|
||||
("kraken", True, 3, 2.2, 0.0025, -6.381165, -0.3198578, margin, 0),
|
||||
("kraken", True, 3, 2.2, 0.0025, -19.143495, -0.3198578, margin, 0),
|
||||
("binance", False, 1, 2.1, 0.003, 2.66100000, 0.044239401, spot, 0),
|
||||
("binance", False, 1, 1.9, 0.003, -3.3209999, -0.055211970, spot, 0),
|
||||
("binance", False, 1, 2.2, 0.003, 5.6520000, 0.093965087, spot, 0),
|
||||
# FUTURES, funding_fee=1
|
||||
("binance", False, 1, 2.1, 0.0025, 3.6925, 0.06138819, futures, 1),
|
||||
("binance", False, 3, 2.1, 0.0025, 3.6925, 0.18416458, futures, 1),
|
||||
("binance", False, 3, 2.1, 0.0025, 9.0775, 0.15091438, futures, 1),
|
||||
("binance", True, 1, 2.1, 0.0025, -2.3074999, -0.03855472, futures, 1),
|
||||
("binance", True, 3, 2.1, 0.0025, -2.3074999, -0.11566416, futures, 1),
|
||||
("binance", True, 3, 2.1, 0.0025, -8.9225, -0.14908104, futures, 1),
|
||||
("binance", False, 1, 1.9, 0.0025, -2.2925, -0.03811305, futures, 1),
|
||||
("binance", False, 3, 1.9, 0.0025, -2.2925, -0.11433915, futures, 1),
|
||||
("binance", False, 3, 1.9, 0.0025, -8.8775, -0.14758936, futures, 1),
|
||||
("binance", True, 1, 1.9, 0.0025, 3.7075, 0.06194653, futures, 1),
|
||||
("binance", True, 3, 1.9, 0.0025, 3.7075, 0.18583959, futures, 1),
|
||||
("binance", True, 3, 1.9, 0.0025, 9.1225, 0.15242272, futures, 1),
|
||||
("binance", False, 1, 2.2, 0.0025, 6.685, 0.11113881, futures, 1),
|
||||
("binance", False, 3, 2.2, 0.0025, 6.685, 0.33341645, futures, 1),
|
||||
("binance", False, 3, 2.2, 0.0025, 18.055, 0.30016625, futures, 1),
|
||||
("binance", True, 1, 2.2, 0.0025, -5.315, -0.08880534, futures, 1),
|
||||
("binance", True, 3, 2.2, 0.0025, -5.315, -0.26641604, futures, 1),
|
||||
("binance", True, 3, 2.2, 0.0025, -17.945, -0.29983292, futures, 1),
|
||||
# FUTURES, funding_fee=-1
|
||||
("binance", False, 1, 2.1, 0.0025, 1.6925, 0.02813798, futures, -1),
|
||||
("binance", False, 3, 2.1, 0.0025, 1.6925, 0.08441396, futures, -1),
|
||||
("binance", False, 3, 2.1, 0.0025, 7.0775, 0.11766417, futures, -1),
|
||||
("binance", True, 1, 2.1, 0.0025, -4.307499, -0.07197159, futures, -1),
|
||||
("binance", True, 3, 2.1, 0.0025, -4.307499, -0.21591478, futures, -1),
|
||||
("binance", True, 3, 2.1, 0.0025, -10.92249, -0.18249791, futures, -1),
|
||||
("binance", False, 1, 1.9, 0.0025, -4.292499, -0.07136325, futures, -1),
|
||||
("binance", False, 3, 1.9, 0.0025, -4.292499, -0.21408977, futures, -1),
|
||||
("binance", False, 3, 1.9, 0.0025, -10.87749, -0.18083957, futures, -1),
|
||||
("binance", True, 1, 1.9, 0.0025, 1.7075, 0.02852965, futures, -1),
|
||||
("binance", True, 3, 1.9, 0.0025, 1.7075, 0.08558897, futures, -1),
|
||||
("binance", True, 3, 1.9, 0.0025, 7.1225, 0.11900585, futures, -1),
|
||||
("binance", False, 1, 2.2, 0.0025, 4.684999, 0.07788861, futures, -1),
|
||||
("binance", False, 3, 2.2, 0.0025, 4.684999, 0.23366583, futures, -1),
|
||||
("binance", False, 3, 2.2, 0.0025, 16.055, 0.26691604, futures, -1),
|
||||
("binance", True, 1, 2.2, 0.0025, -7.315, -0.12222222, futures, -1),
|
||||
("binance", True, 3, 2.2, 0.0025, -7.315, -0.36666666, futures, -1),
|
||||
("binance", True, 3, 2.2, 0.0025, -19.945, -0.33324979, futures, -1),
|
||||
# FUTURES, funding_fee=0
|
||||
("binance", False, 1, 2.1, 0.0025, 2.6925, 0.04476309, futures, 0),
|
||||
("binance", False, 3, 2.1, 0.0025, 2.6925, 0.13428928, futures, 0),
|
||||
("binance", False, 3, 2.1, 0.0025, 8.0775, 0.13428928, futures, 0),
|
||||
("binance", True, 1, 2.1, 0.0025, -3.3074999, -0.05526316, futures, 0),
|
||||
("binance", True, 3, 2.1, 0.0025, -3.3074999, -0.16578947, futures, 0),
|
||||
("binance", True, 3, 2.1, 0.0025, -9.9224997, -0.16578947, futures, 0),
|
||||
("binance", False, 1, 1.9, 0.0025, -3.2925, -0.05473815, futures, 0),
|
||||
("binance", False, 3, 1.9, 0.0025, -3.2925, -0.16421446, futures, 0),
|
||||
("binance", False, 3, 1.9, 0.0025, -9.8775, -0.16421446, futures, 0),
|
||||
("binance", True, 1, 1.9, 0.0025, 2.7075, 0.0452381, futures, 0),
|
||||
("binance", True, 3, 1.9, 0.0025, 2.7075, 0.13571429, futures, 0),
|
||||
("binance", True, 3, 1.9, 0.0025, 8.1225, 0.13571429, futures, 0),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@@ -1162,7 +1162,7 @@ def test_calc_profit(
|
||||
trade = Trade(
|
||||
pair="ADA/USDT",
|
||||
stake_amount=60.0,
|
||||
amount=30.0,
|
||||
amount=30.0 * lev,
|
||||
open_rate=2.0,
|
||||
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
|
||||
interest_rate=0.0005,
|
||||
@@ -1183,7 +1183,7 @@ def test_calc_profit(
|
||||
assert pytest.approx(val) == profit_res.profit_abs
|
||||
|
||||
assert pytest.approx(profit_res.total_profit) == round(profit, 8)
|
||||
# assert pytest.approx(profit_res.total_profit_ratio) == round(profit_ratio, 8)
|
||||
assert pytest.approx(profit_res.total_profit_ratio) == round(profit_ratio, 8)
|
||||
|
||||
assert pytest.approx(trade.calc_profit(rate=close_rate)) == round(profit, 8)
|
||||
assert pytest.approx(trade.calc_profit_ratio(rate=close_rate)) == round(profit_ratio, 8)
|
||||
@@ -1193,7 +1193,7 @@ def test_calc_profit(
|
||||
assert pytest.approx(profit_res2.profit_ratio) == round(profit_ratio, 8)
|
||||
|
||||
assert pytest.approx(profit_res2.total_profit) == round(profit, 8)
|
||||
# assert pytest.approx(profit_res2.total_profit_ratio) == round(profit_ratio, 8)
|
||||
assert pytest.approx(profit_res2.total_profit_ratio) == round(profit_ratio, 8)
|
||||
|
||||
assert pytest.approx(trade.calc_profit(close_rate, trade.amount, trade.open_rate)) == round(
|
||||
profit, 8
|
||||
|
||||
@@ -281,7 +281,7 @@ def test_remove_logs_for_pairs_already_in_blacklist(mocker, markets, static_pl_c
|
||||
|
||||
for _ in range(3):
|
||||
new_whitelist = freqtrade.pairlists.verify_blacklist(
|
||||
whitelist + ["BLK/BTC"], logger.warning
|
||||
[*whitelist, "BLK/BTC"], logger.warning
|
||||
)
|
||||
# Ensure that the pair is removed from the white list, and properly logged.
|
||||
assert set(whitelist) == set(new_whitelist)
|
||||
@@ -2032,11 +2032,7 @@ def test_expand_pairlist(wildcardlist, pairs, expected):
|
||||
},
|
||||
}
|
||||
assert sorted(dynamic_expand_pairlist(conf, pairs)) == sorted(
|
||||
expected
|
||||
+ [
|
||||
"BTC/USDT:USDT",
|
||||
"XRP/BUSD",
|
||||
]
|
||||
[*expected, "BTC/USDT:USDT", "XRP/BUSD"]
|
||||
)
|
||||
|
||||
|
||||
@@ -2138,7 +2134,7 @@ def test_ProducerPairlist(mocker, whitelist_conf, markets):
|
||||
dp = DataProvider(whitelist_conf, exchange, None)
|
||||
pairs = ["ETH/BTC", "LTC/BTC", "XRP/BTC"]
|
||||
# different producer
|
||||
dp._set_producer_pairs(pairs + ["MEEP/USDT"], "default")
|
||||
dp._set_producer_pairs([*pairs, "MEEP/USDT"], "default")
|
||||
pm = PairListManager(exchange, whitelist_conf, dp)
|
||||
pm.refresh_pairlist()
|
||||
assert pm.whitelist == []
|
||||
@@ -2161,7 +2157,7 @@ def test_ProducerPairlist(mocker, whitelist_conf, markets):
|
||||
pm = PairListManager(exchange, whitelist_conf, dp)
|
||||
pm.refresh_pairlist()
|
||||
assert len(pm.whitelist) == 4
|
||||
assert pm.whitelist == ["TKN/BTC"] + pairs
|
||||
assert pm.whitelist == ["TKN/BTC", *pairs]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
|
||||
@@ -19,8 +19,8 @@ def generate_mock_trade(
|
||||
fee: float,
|
||||
is_open: bool,
|
||||
exit_reason: str = ExitType.EXIT_SIGNAL,
|
||||
min_ago_open: int = None,
|
||||
min_ago_close: int = None,
|
||||
min_ago_open: int | None = None,
|
||||
min_ago_close: int | None = None,
|
||||
profit_rate: float = 0.9,
|
||||
is_short: bool = False,
|
||||
):
|
||||
|
||||
@@ -197,7 +197,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
response.update(
|
||||
{
|
||||
"max_stake_amount": 0.001,
|
||||
"total_profit_ratio": pytest.approx(-0.00409153),
|
||||
"total_profit_ratio": pytest.approx(-0.00408133),
|
||||
"has_open_orders": False,
|
||||
}
|
||||
)
|
||||
@@ -228,7 +228,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
assert results[0] == response_norate
|
||||
|
||||
|
||||
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
||||
def test_rpc_status_table(default_conf, ticker, fee, mocker, time_machine) -> None:
|
||||
time_machine.move_to("2024-05-10 11:15:00 +00:00", tick=False)
|
||||
mocker.patch.multiple(
|
||||
"freqtrade.rpc.fiat_convert.FtCoinGeckoApi",
|
||||
get_price=MagicMock(return_value={"bitcoin": {"usd": 15000.0}}),
|
||||
@@ -806,19 +807,19 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
||||
assert freqtradebot.state == State.STOPPED
|
||||
|
||||
|
||||
def test_rpc_stopentry(mocker, default_conf) -> None:
|
||||
def test_rpc_pause(mocker, default_conf) -> None:
|
||||
mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock())
|
||||
mocker.patch.multiple(EXMS, fetch_ticker=MagicMock())
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
rpc = RPC(freqtradebot)
|
||||
freqtradebot.state = State.RUNNING
|
||||
freqtradebot.state = State.PAUSED
|
||||
|
||||
assert freqtradebot.config["max_open_trades"] != 0
|
||||
result = rpc._rpc_stopentry()
|
||||
assert {"status": "No more entries will occur from now. Run /reload_config to reset."} == result
|
||||
assert freqtradebot.config["max_open_trades"] == 0
|
||||
result = rpc._rpc_pause()
|
||||
assert {
|
||||
"status": "paused, no more entries will occur from now. Run /start to enable entries."
|
||||
} == result
|
||||
|
||||
|
||||
def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
|
||||
|
||||
@@ -533,23 +533,26 @@ def test_api_reloadconf(botclient):
|
||||
assert ftbot.state == State.RELOAD_CONFIG
|
||||
|
||||
|
||||
def test_api_stopentry(botclient):
|
||||
def test_api_pause(botclient):
|
||||
ftbot, client = botclient
|
||||
assert ftbot.config["max_open_trades"] != 0
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/stopbuy")
|
||||
rc = client_post(client, f"{BASE_URI}/pause")
|
||||
assert_response(rc)
|
||||
assert rc.json() == {
|
||||
"status": "No more entries will occur from now. Run /reload_config to reset."
|
||||
"status": "paused, no more entries will occur from now. Run /start to enable entries."
|
||||
}
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/pause")
|
||||
assert_response(rc)
|
||||
assert rc.json() == {
|
||||
"status": "paused, no more entries will occur from now. Run /start to enable entries."
|
||||
}
|
||||
assert ftbot.config["max_open_trades"] == 0
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/stopentry")
|
||||
assert_response(rc)
|
||||
assert rc.json() == {
|
||||
"status": "No more entries will occur from now. Run /reload_config to reset."
|
||||
"status": "paused, no more entries will occur from now. Run /start to enable entries."
|
||||
}
|
||||
assert ftbot.config["max_open_trades"] == 0
|
||||
|
||||
|
||||
def test_api_balance(botclient, mocker, rpc_balance, tickers):
|
||||
@@ -773,12 +776,28 @@ def test_api_trades(botclient, mocker, fee, markets, is_short):
|
||||
assert rc.json()["trades_count"] == 2
|
||||
assert rc.json()["total_trades"] == 2
|
||||
assert rc.json()["trades"][0]["is_short"] == is_short
|
||||
# Ensure the trades are sorted by trade_id (the default, see below)
|
||||
assert rc.json()["trades"][0]["trade_id"] == 2
|
||||
assert rc.json()["trades"][1]["trade_id"] == 3
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/trades?limit=1")
|
||||
assert_response(rc)
|
||||
assert len(rc.json()["trades"]) == 1
|
||||
assert rc.json()["trades_count"] == 1
|
||||
assert rc.json()["total_trades"] == 2
|
||||
|
||||
# Test ascending order (default)
|
||||
rc = client_get(client, f"{BASE_URI}/trades?order_by_id=true")
|
||||
assert_response(rc)
|
||||
assert rc.json()["trades"][0]["trade_id"] == 2
|
||||
assert rc.json()["trades"][1]["trade_id"] == 3
|
||||
|
||||
# Test descending order
|
||||
rc = client_get(client, f"{BASE_URI}/trades?order_by_id=false")
|
||||
assert_response(rc)
|
||||
assert rc.json()["trades"][0]["trade_id"] == 3
|
||||
assert rc.json()["trades"][1]["trade_id"] == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("is_short", [True, False])
|
||||
def test_api_trade_single(botclient, mocker, fee, ticker, markets, is_short):
|
||||
@@ -1845,7 +1864,21 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
ohlcv_history["exit_short"] = 0
|
||||
|
||||
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history, CandleType.SPOT)
|
||||
fake_plot_annotations = [
|
||||
{
|
||||
"type": "area",
|
||||
"start": "2024-01-01 15:00:00",
|
||||
"end": "2024-01-01 16:00:00",
|
||||
"y_start": 94000.2,
|
||||
"y_end": 98000,
|
||||
"color": "",
|
||||
"label": "some label",
|
||||
}
|
||||
]
|
||||
plot_annotations_mock = MagicMock(return_value=fake_plot_annotations)
|
||||
ftbot.strategy.plot_annotations = plot_annotations_mock
|
||||
for call in ("get", "post"):
|
||||
plot_annotations_mock.reset_mock()
|
||||
if call == "get":
|
||||
rc = client_get(
|
||||
client,
|
||||
@@ -1875,6 +1908,8 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
assert resp["data_start_ts"] == 1511686200000
|
||||
assert resp["data_stop"] == "2017-11-26 09:00:00+00:00"
|
||||
assert resp["data_stop_ts"] == 1511686800000
|
||||
assert resp["annotations"] == fake_plot_annotations
|
||||
assert plot_annotations_mock.call_count == 1
|
||||
assert isinstance(resp["columns"], list)
|
||||
base_cols = {
|
||||
"date",
|
||||
@@ -2203,8 +2238,8 @@ def test_api_pair_history(botclient, tmp_path, mocker):
|
||||
assert len(result["columns"]) == col_count
|
||||
assert len(result["all_columns"]) == 25
|
||||
assert len(data[0]) == col_count
|
||||
date_col_idx = [idx for idx, c in enumerate(result["columns"]) if c == "date"][0]
|
||||
rsi_col_idx = [idx for idx, c in enumerate(result["columns"]) if c == "rsi"][0]
|
||||
date_col_idx = next(idx for idx, c in enumerate(result["columns"]) if c == "date")
|
||||
rsi_col_idx = next(idx for idx, c in enumerate(result["columns"]) if c == "rsi")
|
||||
|
||||
assert data[0][date_col_idx] == "2018-01-11T00:00:00Z"
|
||||
assert data[0][rsi_col_idx] is not None
|
||||
@@ -2216,6 +2251,7 @@ def test_api_pair_history(botclient, tmp_path, mocker):
|
||||
assert result["data_start_ts"] == 1515628800000
|
||||
assert result["data_stop"] == "2018-01-12 00:00:00+00:00"
|
||||
assert result["data_stop_ts"] == 1515715200000
|
||||
assert result["annotations"] == []
|
||||
lfm.reset_mock()
|
||||
|
||||
# No data found
|
||||
@@ -2429,7 +2465,7 @@ def test_api_exchanges(botclient):
|
||||
response = rc.json()
|
||||
assert isinstance(response["exchanges"], list)
|
||||
assert len(response["exchanges"]) > 20
|
||||
okx = [x for x in response["exchanges"] if x["classname"] == "okx"][0]
|
||||
okx = next(x for x in response["exchanges"] if x["classname"] == "okx")
|
||||
assert okx == {
|
||||
"classname": "okx",
|
||||
"name": "OKX",
|
||||
@@ -2445,7 +2481,7 @@ def test_api_exchanges(botclient):
|
||||
],
|
||||
}
|
||||
|
||||
mexc = [x for x in response["exchanges"] if x["classname"] == "mexc"][0]
|
||||
mexc = next(x for x in response["exchanges"] if x["classname"] == "mexc")
|
||||
assert mexc == {
|
||||
"classname": "mexc",
|
||||
"name": "MEXC Global",
|
||||
@@ -2457,7 +2493,7 @@ def test_api_exchanges(botclient):
|
||||
"alias_for": None,
|
||||
"trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
|
||||
}
|
||||
waves = [x for x in response["exchanges"] if x["classname"] == "wavesexchange"][0]
|
||||
waves = next(x for x in response["exchanges"] if x["classname"] == "wavesexchange")
|
||||
assert waves == {
|
||||
"classname": "wavesexchange",
|
||||
"name": "Waves.Exchange",
|
||||
@@ -2551,10 +2587,10 @@ def test_api_pairlists_available(botclient, tmp_path):
|
||||
assert len([r for r in response["pairlists"] if r["name"] == "VolumePairList"]) == 1
|
||||
assert len([r for r in response["pairlists"] if r["name"] == "StaticPairList"]) == 1
|
||||
|
||||
volumepl = [r for r in response["pairlists"] if r["name"] == "VolumePairList"][0]
|
||||
volumepl = next(r for r in response["pairlists"] if r["name"] == "VolumePairList")
|
||||
assert volumepl["is_pairlist_generator"] is True
|
||||
assert len(volumepl["params"]) > 1
|
||||
age_pl = [r for r in response["pairlists"] if r["name"] == "AgeFilter"][0]
|
||||
age_pl = next(r for r in response["pairlists"] if r["name"] == "AgeFilter")
|
||||
assert age_pl["is_pairlist_generator"] is False
|
||||
assert len(volumepl["params"]) > 2
|
||||
|
||||
@@ -2850,7 +2886,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmp_path):
|
||||
def test_api_backtest_history(botclient, mocker, testdatadir):
|
||||
ftbot, client = botclient
|
||||
mocker.patch(
|
||||
"freqtrade.data.btanalysis._get_backtest_files",
|
||||
"freqtrade.data.btanalysis.bt_fileutils._get_backtest_files",
|
||||
return_value=[
|
||||
testdatadir / "backtest_results/backtest-result_multistrat.json",
|
||||
testdatadir / "backtest_results/backtest-result.json",
|
||||
|
||||
@@ -169,7 +169,7 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
|
||||
"['stats'], ['daily'], ['weekly'], ['monthly'], "
|
||||
"['count'], ['locks'], ['delete_locks', 'unlock'], "
|
||||
"['reload_conf', 'reload_config'], ['show_conf', 'show_config'], "
|
||||
"['stopbuy', 'stopentry'], ['whitelist'], ['blacklist'], "
|
||||
"['pause', 'stopbuy', 'stopentry'], ['whitelist'], ['blacklist'], "
|
||||
"['bl_delete', 'blacklist_delete'], "
|
||||
"['logs'], ['edge'], ['health'], ['help'], ['version'], ['marketdir'], "
|
||||
"['order'], ['list_custom_data'], ['tg_info']]"
|
||||
@@ -1222,15 +1222,15 @@ async def test_stop_handle_already_stopped(default_conf, update, mocker) -> None
|
||||
assert "already stopped" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
async def test_stopbuy_handle(default_conf, update, mocker) -> None:
|
||||
async def test_pause_handle(default_conf, update, mocker) -> None:
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
assert freqtradebot.config["max_open_trades"] != 0
|
||||
await telegram._stopentry(update=update, context=MagicMock())
|
||||
assert freqtradebot.config["max_open_trades"] == 0
|
||||
assert freqtradebot.state == State.RUNNING
|
||||
await telegram._pause(update=update, context=MagicMock())
|
||||
assert freqtradebot.state == State.PAUSED
|
||||
assert msg_mock.call_count == 1
|
||||
assert (
|
||||
"No more entries will occur from now. Run /reload_config to reset."
|
||||
"paused, no more entries will occur from now. Run /start to enable entries."
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
)
|
||||
|
||||
|
||||
@@ -5,11 +5,54 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.commands import Arguments
|
||||
from freqtrade.commands.cli_options import check_int_nonzero, check_int_positive
|
||||
from freqtrade.commands import Arguments, arguments
|
||||
from freqtrade.commands.cli_options import (
|
||||
AVAILABLE_CLI_OPTIONS,
|
||||
check_int_nonzero,
|
||||
check_int_positive,
|
||||
)
|
||||
from tests.conftest import CURRENT_TEST_STRATEGY
|
||||
|
||||
|
||||
def test_available_cli_options():
|
||||
"""
|
||||
AVAILABLE_CLI_OPTIONS has keys that are the union of the values in all ARGS_* - required by CLI
|
||||
each of the ARGS_* lists has a list of members which is assumed to also
|
||||
be in AVAILABLE_CLI_OPTIONS
|
||||
"""
|
||||
args_union = {
|
||||
arg
|
||||
for variable, value in vars(arguments).items()
|
||||
if variable.startswith("ARGS_")
|
||||
for arg in value
|
||||
}
|
||||
expected_options = set(AVAILABLE_CLI_OPTIONS)
|
||||
only_in_command_args = expected_options.difference(args_union)
|
||||
only_in_all_args = args_union.difference(expected_options)
|
||||
if only_in_all_args or only_in_command_args:
|
||||
pytest.fail(
|
||||
"variables around command line arguments not kept in sync:\n"
|
||||
f" * args only in some ARGS_* list but not AVAILABLE_CLI_OPTIONS: {only_in_all_args}\n"
|
||||
" * args only in AVAILABLE_CLI_OPTIONS but not some ARGS_* list: "
|
||||
f"{only_in_command_args}"
|
||||
)
|
||||
|
||||
|
||||
def test_arguments_match_available_cli_options(monkeypatch):
|
||||
"""All entries in AVAILABLE_CLI_OPTIONS are used in argument parsing."""
|
||||
parsed_options = set()
|
||||
actual_build_args = Arguments._build_args
|
||||
|
||||
def build_args_monitor(self, optionlist, parser):
|
||||
parsed_options.update(optionlist)
|
||||
return actual_build_args(self, optionlist=optionlist, parser=parser)
|
||||
|
||||
monkeypatch.setattr(Arguments, "_build_args", build_args_monitor)
|
||||
# this will result in a parser being built so we can check the arguments used
|
||||
Arguments([]).get_parsed_arg()
|
||||
assert parsed_options == set(AVAILABLE_CLI_OPTIONS)
|
||||
|
||||
|
||||
# Parse common command-line-arguments. Used for all tools
|
||||
def test_parse_args_none() -> None:
|
||||
arguments = Arguments(["trade"])
|
||||
|
||||
Reference in New Issue
Block a user