freqtrade_origin/tests/data/test_entryexitanalysis.py
2024-09-11 15:33:26 +05:30

563 lines
18 KiB
Python

import logging
from unittest.mock import MagicMock, PropertyMock
import pandas as pd
import pytest
from freqtrade.commands.analyze_commands import start_analysis_entries_exits
from freqtrade.commands.optimize_commands import start_backtesting
from freqtrade.enums import ExitType
from freqtrade.exceptions import OperationalException
from freqtrade.optimize.backtesting import Backtesting
from tests.conftest import get_args, patch_exchange, patched_configuration_load_config_file
@pytest.fixture(autouse=True)
def entryexitanalysis_cleanup() -> None:
yield None
Backtesting.cleanup()
def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
default_conf, mocker, caplog, testdatadir, user_dir, capsys
):
caplog.set_level(logging.INFO)
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
default_conf.update(
{
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
}
)
patch_exchange(mocker)
result1 = pd.DataFrame(
{
"pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"],
"profit_ratio": [0.025, 0.05, -0.1, -0.05],
"profit_abs": [0.5, 2.0, -4.0, -2.0],
"open_date": pd.to_datetime(
[
"2018-01-29 18:40:00",
"2018-01-30 03:30:00",
"2018-01-30 08:10:00",
"2018-01-31 13:30:00",
],
utc=True,
),
"close_date": pd.to_datetime(
[
"2018-01-29 20:45:00",
"2018-01-30 05:35:00",
"2018-01-30 09:10:00",
"2018-01-31 15:00:00",
],
utc=True,
),
"trade_duration": [235, 40, 60, 90],
"is_open": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485],
"close_rate": [0.104969, 0.103541, 0.102041, 0.102541],
"is_short": [False, False, False, False],
"enter_tag": [
"enter_tag_long_a",
"enter_tag_long_b",
"enter_tag_long_a",
"enter_tag_long_b",
],
"exit_reason": [
ExitType.ROI.value,
ExitType.EXIT_SIGNAL.value,
ExitType.STOP_LOSS.value,
ExitType.TRAILING_STOP_LOSS.value,
],
}
)
backtestmock = MagicMock(
side_effect=[
{
"results": result1,
"config": default_conf,
"locks": [],
"rejected_signals": 20,
"timedout_entry_orders": 0,
"timedout_exit_orders": 0,
"canceled_trade_entries": 0,
"canceled_entry_orders": 0,
"replaced_entry_orders": 0,
"final_balance": 1000,
}
]
)
mocker.patch(
"freqtrade.plugins.pairlistmanager.PairListManager.whitelist",
PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]),
)
mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock)
patched_configuration_load_config_file(mocker, default_conf)
args = [
"backtesting",
"--config",
"config.json",
"--datadir",
str(testdatadir),
"--user-data-dir",
str(user_dir),
"--timeframe",
"5m",
"--timerange",
"1515560100-1517287800",
"--export",
"signals",
"--cache",
"none",
]
args = get_args(args)
start_backtesting(args)
captured = capsys.readouterr()
assert "BACKTESTING REPORT" in captured.out
assert "EXIT REASON STATS" in captured.out
assert "LEFT OPEN TRADES REPORT" in captured.out
base_args = [
"backtesting-analysis",
"--config",
"config.json",
"--datadir",
str(testdatadir),
"--user-data-dir",
str(user_dir),
]
# test group 0 and indicator list
args = get_args(
base_args + ["--analysis-groups", "0", "--indicator-list", "close", "rsi", "profit_abs"]
)
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "LTC/BTC" in captured.out
assert "ETH/BTC" in captured.out
assert "enter_tag_long_a" in captured.out
assert "enter_tag_long_b" in captured.out
assert "exit_signal" in captured.out
assert "roi" in captured.out
assert "stop_loss" in captured.out
assert "trailing_stop_loss" in captured.out
assert "0.5" in captured.out
assert "-4" in captured.out
assert "-2" in captured.out
assert "-3.5" in captured.out
assert "50" in captured.out
assert "0" in captured.out
assert "0.016" in captured.out
assert "34.049" in captured.out
assert "0.104" in captured.out
assert "52.829" in captured.out
# assert indicator list
assert "close (entry)" in captured.out
assert "0.016" in captured.out
assert "rsi (entry)" in captured.out
assert "54.320" in captured.out
assert "close (exit)" in captured.out
assert "rsi (exit)" in captured.out
assert "52.829" in captured.out
assert "profit_abs" in captured.out
# test group 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
assert "enter_tag_long_b" in captured.out
assert "total_profit_pct" in captured.out
assert "-3.5" in captured.out
assert "-1.75" in captured.out
assert "-7.5" in captured.out
assert "-3.75" in captured.out
assert "0" in captured.out
# test group 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
assert "enter_tag_long_b" in captured.out
assert "exit_signal" in captured.out
assert "roi" in captured.out
assert "stop_loss" in captured.out
assert "trailing_stop_loss" in captured.out
assert "total_profit_pct" in captured.out
assert "-10" in captured.out
assert "-5" in captured.out
assert "2.5" in captured.out
# test group 3
args = get_args(base_args + ["--analysis-groups", "3"])
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "LTC/BTC" in captured.out
assert "ETH/BTC" in captured.out
assert "enter_tag_long_a" in captured.out
assert "enter_tag_long_b" in captured.out
assert "total_profit_pct" in captured.out
assert "-7.5" in captured.out
assert "-3.75" in captured.out
assert "-1.75" in captured.out
assert "0" in captured.out
assert "2" in captured.out
# test group 4
args = get_args(base_args + ["--analysis-groups", "4"])
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "LTC/BTC" in captured.out
assert "ETH/BTC" in captured.out
assert "enter_tag_long_a" in captured.out
assert "enter_tag_long_b" in captured.out
assert "exit_signal" in captured.out
assert "roi" in captured.out
assert "stop_loss" in captured.out
assert "trailing_stop_loss" in captured.out
assert "total_profit_pct" in captured.out
assert "-10" in captured.out
assert "-5" in captured.out
assert "-4" in captured.out
assert "0.5" in captured.out
assert "1" in captured.out
assert "2.5" in captured.out
# test group 5
args = get_args(base_args + ["--analysis-groups", "5"])
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "exit_signal" in captured.out
assert "roi" in captured.out
assert "stop_loss" in captured.out
assert "trailing_stop_loss" in captured.out
# test date filtering
args = get_args(
base_args + ["--analysis-groups", "0", "1", "2", "--timerange", "20180129-20180130"]
)
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "enter_tag_long_a" in captured.out
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"])
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "no rejected signals" in captured.out
def test_backtest_analysis_with_invalid_config(
default_conf, mocker, caplog, testdatadir, user_dir, capsys
):
caplog.set_level(logging.INFO)
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
default_conf.update(
{
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
}
)
patch_exchange(mocker)
result1 = pd.DataFrame(
{
"pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"],
"profit_ratio": [0.025, 0.05, -0.1, -0.05],
"profit_abs": [0.5, 2.0, -4.0, -2.0],
"open_date": pd.to_datetime(
[
"2018-01-29 18:40:00",
"2018-01-30 03:30:00",
"2018-01-30 08:10:00",
"2018-01-31 13:30:00",
],
utc=True,
),
"close_date": pd.to_datetime(
[
"2018-01-29 20:45:00",
"2018-01-30 05:35:00",
"2018-01-30 09:10:00",
"2018-01-31 15:00:00",
],
utc=True,
),
"trade_duration": [235, 40, 60, 90],
"is_open": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485],
"close_rate": [0.104969, 0.103541, 0.102041, 0.102541],
"is_short": [False, False, False, False],
"enter_tag": [
"enter_tag_long_a",
"enter_tag_long_b",
"enter_tag_long_a",
"enter_tag_long_b",
],
"exit_reason": [
ExitType.ROI.value,
ExitType.EXIT_SIGNAL.value,
ExitType.STOP_LOSS.value,
ExitType.TRAILING_STOP_LOSS.value,
],
}
)
backtestmock = MagicMock(
side_effect=[
{
"results": result1,
"config": default_conf,
"locks": [],
"rejected_signals": 20,
"timedout_entry_orders": 0,
"timedout_exit_orders": 0,
"canceled_trade_entries": 0,
"canceled_entry_orders": 0,
"replaced_entry_orders": 0,
"final_balance": 1000,
}
]
)
mocker.patch(
"freqtrade.plugins.pairlistmanager.PairListManager.whitelist",
PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]),
)
mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock)
patched_configuration_load_config_file(mocker, default_conf)
args = [
"backtesting",
"--config",
"config.json",
"--datadir",
str(testdatadir),
"--user-data-dir",
str(user_dir),
"--timeframe",
"5m",
"--timerange",
"1515560100-1517287800",
"--export",
"signals",
"--cache",
"none",
]
args = get_args(args)
start_backtesting(args)
captured = capsys.readouterr()
assert "BACKTESTING REPORT" in captured.out
assert "EXIT REASON STATS" in captured.out
assert "LEFT OPEN TRADES REPORT" in captured.out
base_args = [
"backtesting-analysis",
"--config",
"config.json",
"--datadir",
str(testdatadir),
"--user-data-dir",
str(user_dir),
]
# test with both entry and exit only arguments
args = get_args(
base_args
+ [
"--analysis-groups",
"0",
"--indicator-list",
"close",
"rsi",
"profit_abs",
"--entry-only",
"--exit-only",
]
)
with pytest.raises(
OperationalException,
match=r"Cannot use --entry-only and --exit-only at the same time. Please choose one.",
):
start_analysis_entries_exits(args)
def test_backtest_analysis_on_entry_and_rejected_signals_only_entry_signals(
default_conf, mocker, caplog, testdatadir, user_dir, capsys
):
caplog.set_level(logging.INFO)
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
default_conf.update(
{
"use_exit_signal": True,
"exit_profit_only": False,
"exit_profit_offset": 0.0,
"ignore_roi_if_entry_signal": False,
}
)
patch_exchange(mocker)
result1 = pd.DataFrame(
{
"pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"],
"profit_ratio": [0.025, 0.05, -0.1, -0.05],
"profit_abs": [0.5, 2.0, -4.0, -2.0],
"open_date": pd.to_datetime(
[
"2018-01-29 18:40:00",
"2018-01-30 03:30:00",
"2018-01-30 08:10:00",
"2018-01-31 13:30:00",
],
utc=True,
),
"close_date": pd.to_datetime(
[
"2018-01-29 20:45:00",
"2018-01-30 05:35:00",
"2018-01-30 09:10:00",
"2018-01-31 15:00:00",
],
utc=True,
),
"trade_duration": [235, 40, 60, 90],
"is_open": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485],
"close_rate": [0.104969, 0.103541, 0.102041, 0.102541],
"is_short": [False, False, False, False],
"enter_tag": [
"enter_tag_long_a",
"enter_tag_long_b",
"enter_tag_long_a",
"enter_tag_long_b",
],
"exit_reason": [
ExitType.ROI.value,
ExitType.EXIT_SIGNAL.value,
ExitType.STOP_LOSS.value,
ExitType.TRAILING_STOP_LOSS.value,
],
}
)
backtestmock = MagicMock(
side_effect=[
{
"results": result1,
"config": default_conf,
"locks": [],
"rejected_signals": 20,
"timedout_entry_orders": 0,
"timedout_exit_orders": 0,
"canceled_trade_entries": 0,
"canceled_entry_orders": 0,
"replaced_entry_orders": 0,
"final_balance": 1000,
}
]
)
mocker.patch(
"freqtrade.plugins.pairlistmanager.PairListManager.whitelist",
PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]),
)
mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock)
patched_configuration_load_config_file(mocker, default_conf)
args = [
"backtesting",
"--config",
"config.json",
"--datadir",
str(testdatadir),
"--user-data-dir",
str(user_dir),
"--timeframe",
"5m",
"--timerange",
"1515560100-1517287800",
"--export",
"signals",
"--cache",
"none",
]
args = get_args(args)
start_backtesting(args)
captured = capsys.readouterr()
assert "BACKTESTING REPORT" in captured.out
assert "EXIT REASON STATS" in captured.out
assert "LEFT OPEN TRADES REPORT" in captured.out
base_args = [
"backtesting-analysis",
"--config",
"config.json",
"--datadir",
str(testdatadir),
"--user-data-dir",
str(user_dir),
]
# test group 0 and indicator list
args = get_args(
base_args
+ [
"--analysis-groups",
"0",
"--indicator-list",
"close",
"rsi",
"profit_abs",
"--entry-only",
]
)
start_analysis_entries_exits(args)
captured = capsys.readouterr()
assert "LTC/BTC" in captured.out
assert "ETH/BTC" in captured.out
assert "enter_tag_long_a" in captured.out
assert "enter_tag_long_b" in captured.out
assert "exit_signal" in captured.out
assert "roi" in captured.out
assert "stop_loss" in captured.out
assert "trailing_stop_loss" in captured.out
assert "0.5" in captured.out
assert "-4" in captured.out
assert "-2" in captured.out
assert "-3.5" in captured.out
assert "50" in captured.out
assert "0" in captured.out
assert "0.016" in captured.out
assert "34.049" in captured.out
assert "0.104" in captured.out
assert "52.829" in captured.out
# assert indicator list
assert "close" in captured.out
assert "close (entry)" not in captured.out
assert "0.016" in captured.out
assert "rsi (entry)" not in captured.out
assert "rsi" in captured.out
assert "54.320" in captured.out
assert "close (exit)" not in captured.out
assert "rsi (exit)" not in captured.out
assert "52.829" in captured.out
assert "profit_abs" in captured.out