freqtrade_origin/tests/optimize/test_optimize_reports.py

591 lines
22 KiB
Python
Raw Normal View History

2020-06-28 07:27:19 +00:00
import re
2022-04-19 12:08:01 +00:00
from datetime import timedelta
from pathlib import Path
2022-09-23 05:09:34 +00:00
from shutil import copyfile
import joblib
import pandas as pd
2020-05-25 05:14:21 +00:00
import pytest
2020-09-28 17:43:15 +00:00
2020-06-26 18:08:45 +00:00
from freqtrade.configuration import TimeRange
2023-04-24 07:41:36 +00:00
from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN
2020-06-26 18:08:45 +00:00
from freqtrade.data import history
2024-05-12 13:08:40 +00:00
from freqtrade.data.btanalysis import (
get_latest_backtest_filename,
load_backtest_data,
load_backtest_stats,
)
2020-06-28 07:27:19 +00:00
from freqtrade.edge import PairInfo
2022-03-25 05:55:37 +00:00
from freqtrade.enums import ExitType
2024-05-12 13:08:40 +00:00
from freqtrade.optimize.optimize_reports import (
generate_backtest_stats,
generate_daily_stats,
generate_edge_table,
generate_pair_metrics,
generate_periodic_breakdown_stats,
generate_strategy_comparison,
generate_trading_stats,
show_sorted_pairlist,
store_backtest_analysis_results,
store_backtest_stats,
text_table_bt_results,
text_table_strategy,
)
2024-03-30 12:28:13 +00:00
from freqtrade.optimize.optimize_reports.bt_output import text_table_tags
2024-05-12 13:08:40 +00:00
from freqtrade.optimize.optimize_reports.optimize_reports import (
_get_resample_from_period,
calc_streak,
generate_tag_metrics,
)
from freqtrade.resolvers.strategy_resolver import StrategyResolver
2023-05-14 09:19:36 +00:00
from freqtrade.util import dt_ts
2023-05-14 15:46:56 +00:00
from freqtrade.util.datetime_helpers import dt_from_ts, dt_utc
from tests.conftest import CURRENT_TEST_STRATEGY
2022-09-23 05:09:34 +00:00
from tests.data.test_history import _clean_test_file
def _backup_file(file: Path, copy_file: bool = False) -> None:
"""
Backup existing file to avoid deleting the user file
:param file: complete path to the file
:param copy_file: keep file in place too.
:return: None
"""
2024-05-12 14:04:01 +00:00
file_swp = str(file) + ".swp"
2022-09-23 05:09:34 +00:00
if file.is_file():
file.rename(file_swp)
if copy_file:
copyfile(file_swp, file)
2024-07-09 04:43:12 +00:00
def test_text_table_bt_results(capsys):
results = pd.DataFrame(
{
2024-05-12 14:04:01 +00:00
"pair": ["ETH/BTC", "ETH/BTC", "ETH/BTC"],
"profit_ratio": [0.1, 0.2, -0.05],
"profit_abs": [0.2, 0.4, -0.1],
"trade_duration": [10, 30, 20],
}
)
2024-05-12 14:04:01 +00:00
pair_results = generate_pair_metrics(
["ETH/BTC"], stake_currency="BTC", starting_balance=4, results=results
)
2024-07-09 04:43:12 +00:00
text_table_bt_results(pair_results, stake_currency="BTC", title="title")
text = capsys.readouterr().out
re.search(
r".* Pair .* Trades .* Avg Profit % .* Tot Profit BTC .* Tot Profit % .* "
r"Avg Duration .* Win Draw Loss Win% .*",
text,
)
re.search(
r".* ETH/BTC .* 3 .* 8.33 .* 0.50000000 .* 12.50 .* 0:20:00 .* 2 0 1 66.7 .*",
text,
)
re.search(
r".* TOTAL .* 3 .* 8.33 .* 0.50000000 .* 12.50 .* 0:20:00 .* 2 0 1 66.7 .*", text
)
2023-11-05 15:25:23 +00:00
def test_generate_backtest_stats(default_conf, testdatadir, tmp_path):
2024-05-12 14:04:01 +00:00
default_conf.update({"strategy": CURRENT_TEST_STRATEGY})
StrategyResolver.load_strategy(default_conf)
2024-05-12 14:04:01 +00:00
results = {
"DefStrat": {
"results": pd.DataFrame(
{
"pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"],
"profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, 0.000014, 0.000003],
"open_date": [
dt_utc(2017, 11, 14, 19, 32, 00),
dt_utc(2017, 11, 14, 21, 36, 00),
dt_utc(2017, 11, 14, 22, 12, 00),
dt_utc(2017, 11, 14, 22, 44, 00),
],
"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),
dt_utc(2017, 11, 14, 22, 58, 00),
],
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
"trade_duration": [123, 34, 31, 14],
"is_open": [False, False, False, True],
"is_short": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"exit_reason": [
ExitType.ROI.value,
ExitType.STOP_LOSS.value,
ExitType.ROI.value,
ExitType.FORCE_EXIT.value,
2024-05-12 14:04:01 +00:00
],
}
),
"config": default_conf,
"locks": [],
"final_balance": 1000.02,
"rejected_signals": 20,
"timedout_entry_orders": 0,
"timedout_exit_orders": 0,
"canceled_trade_entries": 0,
"canceled_entry_orders": 0,
"replaced_entry_orders": 0,
"backtest_start_time": dt_ts() // 1000,
"backtest_end_time": dt_ts() // 1000,
"run_id": "123",
2021-01-13 06:47:03 +00:00
}
2024-05-12 14:04:01 +00:00
}
timerange = TimeRange.parse_timerange("1510688220-1510700340")
2023-05-14 15:46:56 +00:00
min_date = dt_from_ts(1510688220)
max_date = dt_from_ts(1510700340)
2024-05-12 14:04:01 +00:00
btdata = history.load_data(
testdatadir, "1m", ["UNITTEST/BTC"], timerange=timerange, fill_up_missing=True
)
2020-06-26 18:08:45 +00:00
stats = generate_backtest_stats(btdata, results, min_date, max_date)
2020-06-26 18:08:45 +00:00
assert isinstance(stats, dict)
2024-05-12 14:04:01 +00:00
assert "strategy" in stats
assert "DefStrat" in stats["strategy"]
assert "strategy_comparison" in stats
strat_stats = stats["strategy"]["DefStrat"]
assert strat_stats["backtest_start"] == min_date.strftime(DATETIME_PRINT_FORMAT)
assert strat_stats["backtest_end"] == max_date.strftime(DATETIME_PRINT_FORMAT)
assert strat_stats["total_trades"] == len(results["DefStrat"]["results"])
2024-04-18 20:51:25 +00:00
# Above sample had no losing trade
2024-05-12 14:04:01 +00:00
assert strat_stats["max_drawdown_account"] == 0.0
2020-06-26 18:08:45 +00:00
2021-02-27 09:14:25 +00:00
# Retry with losing trade
2024-05-12 14:04:01 +00:00
results = {
"DefStrat": {
"results": pd.DataFrame(
{
"pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"],
"profit_ratio": [0.003312, 0.010801, -0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, -0.000014, 0.000003],
"open_date": [
dt_utc(2017, 11, 14, 19, 32, 00),
dt_utc(2017, 11, 14, 21, 36, 00),
dt_utc(2017, 11, 14, 22, 12, 00),
dt_utc(2017, 11, 14, 22, 44, 00),
],
"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),
dt_utc(2017, 11, 14, 22, 58, 00),
],
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
"close_rate": [0.002546, 0.003014, 0.0032903, 0.003217],
"trade_duration": [123, 34, 31, 14],
"is_open": [False, False, False, True],
"is_short": [False, False, False, False],
"stake_amount": [0.01, 0.01, 0.01, 0.01],
"exit_reason": [
ExitType.ROI.value,
ExitType.ROI.value,
ExitType.STOP_LOSS.value,
ExitType.FORCE_EXIT.value,
2024-05-12 14:04:01 +00:00
],
}
),
"config": default_conf,
"locks": [],
"final_balance": 1000.02,
"rejected_signals": 20,
"timedout_entry_orders": 0,
"timedout_exit_orders": 0,
"canceled_trade_entries": 0,
"canceled_entry_orders": 0,
"replaced_entry_orders": 0,
"backtest_start_time": dt_ts() // 1000,
"backtest_end_time": dt_ts() // 1000,
"run_id": "124",
2021-02-27 09:14:25 +00:00
}
}
2020-06-26 18:08:45 +00:00
2021-02-27 09:14:25 +00:00
stats = generate_backtest_stats(btdata, results, min_date, max_date)
assert isinstance(stats, dict)
2024-05-12 14:04:01 +00:00
assert "strategy" in stats
assert "DefStrat" in stats["strategy"]
assert "strategy_comparison" in stats
strat_stats = stats["strategy"]["DefStrat"]
assert pytest.approx(strat_stats["max_drawdown_account"]) == 1.399999e-08
assert strat_stats["drawdown_start"] == "2017-11-14 22:10:00"
assert strat_stats["drawdown_end"] == "2017-11-14 22:43:00"
assert strat_stats["drawdown_end_ts"] == 1510699380000
assert strat_stats["drawdown_start_ts"] == 1510697400000
assert strat_stats["pairlist"] == ["UNITTEST/BTC"]
2020-06-26 18:08:45 +00:00
2020-06-27 17:48:45 +00:00
# Test storing stats
2024-05-12 14:04:01 +00:00
filename = tmp_path / "btresult.json"
2023-11-05 15:25:23 +00:00
filename_last = tmp_path / LAST_BT_RESULT_FN
2020-06-27 17:48:45 +00:00
_backup_file(filename_last, copy_file=True)
assert not filename.is_file()
2024-05-12 14:04:01 +00:00
store_backtest_stats(filename, stats, "2022_01_01_15_05_13")
2020-06-27 17:48:45 +00:00
# get real Filename (it's btresult-<date>.json)
last_fn = get_latest_backtest_filename(filename_last.parent)
assert re.match(r"btresult-.*\.json", last_fn)
2023-11-05 15:25:23 +00:00
filename1 = tmp_path / last_fn
2020-06-27 17:48:45 +00:00
assert filename1.is_file()
content = filename1.read_text()
2024-05-12 14:04:01 +00:00
assert "max_drawdown_account" in content
assert "strategy" in content
assert "pairlist" in content
2020-06-27 17:48:45 +00:00
assert filename_last.is_file()
_clean_test_file(filename_last)
filename1.unlink()
2020-06-26 18:08:45 +00:00
2020-08-18 13:20:37 +00:00
def test_store_backtest_stats(testdatadir, mocker):
2024-05-12 14:04:01 +00:00
dump_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_json")
2020-08-18 13:20:37 +00:00
2024-05-12 14:04:01 +00:00
data = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
store_backtest_stats(testdatadir, data, "2022_01_01_15_05_13")
2020-08-18 13:20:37 +00:00
assert dump_mock.call_count == 3
2020-08-18 13:20:37 +00:00
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
2024-05-12 14:04:01 +00:00
assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / "backtest-result"))
2020-08-18 13:20:37 +00:00
dump_mock.reset_mock()
2024-05-12 14:04:01 +00:00
filename = testdatadir / "testresult.json"
store_backtest_stats(filename, data, "2022_01_01_15_05_13")
assert dump_mock.call_count == 3
2020-08-18 13:20:37 +00:00
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
# result will be testdatadir / testresult-<timestamp>.json
2024-05-12 14:04:01 +00:00
assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / "testresult"))
2020-08-18 13:20:37 +00:00
2024-04-17 05:01:32 +00:00
def test_store_backtest_stats_real(tmp_path):
2024-05-12 14:04:01 +00:00
data = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
store_backtest_stats(tmp_path, data, "2022_01_01_15_05_13")
2024-04-17 05:01:32 +00:00
2024-05-12 14:04:01 +00:00
assert (tmp_path / "backtest-result-2022_01_01_15_05_13.json").is_file()
assert (tmp_path / "backtest-result-2022_01_01_15_05_13.meta.json").is_file()
assert not (tmp_path / "backtest-result-2022_01_01_15_05_13_market_change.feather").is_file()
2024-04-17 05:01:32 +00:00
assert (tmp_path / LAST_BT_RESULT_FN).is_file()
fn = get_latest_backtest_filename(tmp_path)
2024-05-12 14:04:01 +00:00
assert fn == "backtest-result-2022_01_01_15_05_13.json"
2024-04-17 05:01:32 +00:00
2024-05-12 14:04:01 +00:00
store_backtest_stats(tmp_path, data, "2024_01_01_15_05_25", market_change_data=pd.DataFrame())
assert (tmp_path / "backtest-result-2024_01_01_15_05_25.json").is_file()
assert (tmp_path / "backtest-result-2024_01_01_15_05_25.meta.json").is_file()
assert (tmp_path / "backtest-result-2024_01_01_15_05_25_market_change.feather").is_file()
2024-04-17 05:01:32 +00:00
assert (tmp_path / LAST_BT_RESULT_FN).is_file()
# Last file reference should be updated
fn = get_latest_backtest_filename(tmp_path)
2024-05-12 14:04:01 +00:00
assert fn == "backtest-result-2024_01_01_15_05_25.json"
2024-04-17 05:01:32 +00:00
def test_store_backtest_candles(testdatadir, mocker):
2024-05-12 14:04:01 +00:00
dump_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_joblib")
2024-05-12 14:04:01 +00:00
candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}}
# mock directory exporting
2024-05-12 14:04:01 +00:00
store_backtest_analysis_results(testdatadir, candle_dict, {}, "2022_01_01_15_05_13")
2023-03-19 13:56:41 +00:00
assert dump_mock.call_count == 2
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
2024-05-12 14:04:01 +00:00
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
dump_mock.reset_mock()
# mock file exporting
2024-05-12 14:04:01 +00:00
filename = Path(testdatadir / "testresult")
store_backtest_analysis_results(filename, candle_dict, {}, "2022_01_01_15_05_13")
2023-03-19 13:56:41 +00:00
assert dump_mock.call_count == 2
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
# result will be testdatadir / testresult-<timestamp>_signals.pkl
2024-05-12 14:04:01 +00:00
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
dump_mock.reset_mock()
2023-11-05 15:25:23 +00:00
def test_write_read_backtest_candles(tmp_path):
2024-05-12 14:04:01 +00:00
candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}}
# test directory exporting
2024-05-12 14:04:01 +00:00
sample_date = "2022_01_01_15_05_13"
2023-11-05 15:25:23 +00:00
store_backtest_analysis_results(tmp_path, candle_dict, {}, sample_date)
2024-05-12 14:04:01 +00:00
stored_file = tmp_path / f"backtest-result-{sample_date}_signals.pkl"
with stored_file.open("rb") as scp:
pickled_signal_candles = joblib.load(scp)
assert pickled_signal_candles.keys() == candle_dict.keys()
2024-05-12 14:04:01 +00:00
assert pickled_signal_candles["DefStrat"].keys() == pickled_signal_candles["DefStrat"].keys()
assert pickled_signal_candles["DefStrat"]["UNITTEST/BTC"].equals(
pickled_signal_candles["DefStrat"]["UNITTEST/BTC"]
)
_clean_test_file(stored_file)
# test file exporting
2024-05-12 14:04:01 +00:00
filename = tmp_path / "testresult"
2023-03-19 13:56:41 +00:00
store_backtest_analysis_results(filename, candle_dict, {}, sample_date)
2024-05-12 14:04:01 +00:00
stored_file = tmp_path / f"testresult-{sample_date}_signals.pkl"
with stored_file.open("rb") as scp:
pickled_signal_candles = joblib.load(scp)
assert pickled_signal_candles.keys() == candle_dict.keys()
2024-05-12 14:04:01 +00:00
assert pickled_signal_candles["DefStrat"].keys() == pickled_signal_candles["DefStrat"].keys()
assert pickled_signal_candles["DefStrat"]["UNITTEST/BTC"].equals(
pickled_signal_candles["DefStrat"]["UNITTEST/BTC"]
)
_clean_test_file(stored_file)
2020-09-26 12:55:12 +00:00
def test_generate_pair_metrics():
2020-05-25 17:50:09 +00:00
results = pd.DataFrame(
{
2024-05-12 14:04:01 +00:00
"pair": ["ETH/BTC", "ETH/BTC"],
"profit_ratio": [0.1, 0.2],
"profit_abs": [0.2, 0.4],
"trade_duration": [10, 30],
"wins": [2, 0],
"draws": [0, 0],
"losses": [0, 0],
2020-05-25 17:50:09 +00:00
}
)
2024-05-12 14:04:01 +00:00
pair_results = generate_pair_metrics(
["ETH/BTC"], stake_currency="BTC", starting_balance=2, results=results
)
2020-05-25 17:50:09 +00:00
assert isinstance(pair_results, list)
assert len(pair_results) == 2
2024-05-12 14:04:01 +00:00
assert pair_results[-1]["key"] == "TOTAL"
2020-05-25 17:50:09 +00:00
assert (
2024-05-12 14:04:01 +00:00
pytest.approx(pair_results[-1]["profit_mean_pct"]) == pair_results[-1]["profit_mean"] * 100
)
assert pytest.approx(pair_results[-1]["profit_sum_pct"]) == pair_results[-1]["profit_sum"] * 100
2020-05-25 17:50:09 +00:00
2020-07-03 18:03:33 +00:00
def test_generate_daily_stats(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
2020-07-03 18:03:33 +00:00
bt_data = load_backtest_data(filename)
res = generate_daily_stats(bt_data)
assert isinstance(res, dict)
2024-05-12 14:04:01 +00:00
assert round(res["backtest_best_day"], 4) == 0.1796
assert round(res["backtest_worst_day"], 4) == -0.1468
assert res["winning_days"] == 19
assert res["draw_days"] == 0
assert res["losing_days"] == 2
2020-07-03 18:03:33 +00:00
2020-08-20 17:56:41 +00:00
# Select empty dataframe!
2024-05-12 14:04:01 +00:00
res = generate_daily_stats(bt_data.loc[bt_data["open_date"] == "2000-01-01", :])
2020-08-20 17:56:41 +00:00
assert isinstance(res, dict)
2024-05-12 14:04:01 +00:00
assert round(res["backtest_best_day"], 4) == 0.0
assert res["winning_days"] == 0
assert res["draw_days"] == 0
assert res["losing_days"] == 0
2020-08-20 17:56:41 +00:00
2020-07-03 18:03:33 +00:00
def test_generate_trading_stats(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename)
res = generate_trading_stats(bt_data)
assert isinstance(res, dict)
2024-05-12 14:04:01 +00:00
assert res["winner_holding_avg"] == timedelta(seconds=1440)
assert res["loser_holding_avg"] == timedelta(days=1, seconds=21420)
assert "wins" in res
assert "losses" in res
assert "draws" in res
# Select empty dataframe!
2024-05-12 14:04:01 +00:00
res = generate_trading_stats(bt_data.loc[bt_data["open_date"] == "2000-01-01", :])
assert res["wins"] == 0
assert res["losses"] == 0
2023-07-24 05:09:11 +00:00
def test_calc_streak(testdatadir):
2024-05-12 14:04:01 +00:00
df = pd.DataFrame(
{
"profit_ratio": [0.05, -0.02, -0.03, -0.05, 0.01, 0.02, 0.03, 0.04, -0.02, -0.03],
}
)
# 4 consecutive wins, 3 consecutive losses
2023-07-24 05:09:11 +00:00
res = calc_streak(df)
assert res == (4, 3)
assert isinstance(res[0], int)
assert isinstance(res[1], int)
# invert situation
df1 = df.copy()
2024-05-12 14:04:01 +00:00
df1["profit_ratio"] = df1["profit_ratio"] * -1
2023-07-24 05:09:11 +00:00
assert calc_streak(df1) == (3, 4)
2024-05-12 14:04:01 +00:00
df_empty = pd.DataFrame(
{
"profit_ratio": [],
}
)
assert df_empty.empty
2023-07-24 05:09:11 +00:00
assert calc_streak(df_empty) == (0, 0)
filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename)
2023-07-24 05:09:11 +00:00
assert calc_streak(bt_data) == (7, 18)
2024-07-09 04:49:33 +00:00
def test_text_table_exit_reason(capsys):
results = pd.DataFrame(
{
2024-05-12 14:04:01 +00:00
"pair": ["ETH/BTC", "ETH/BTC", "ETH/BTC"],
"profit_ratio": [0.1, 0.2, -0.1],
"profit_abs": [0.2, 0.4, -0.2],
"trade_duration": [10, 30, 10],
"wins": [2, 0, 0],
"draws": [0, 0, 0],
"losses": [0, 0, 1],
"exit_reason": [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value],
}
)
2024-05-12 14:04:01 +00:00
exit_reason_stats = generate_tag_metrics(
"exit_reason", starting_balance=22, results=results, skip_nan=False
)
2024-07-09 04:49:33 +00:00
text_table_tags("exit_tag", exit_reason_stats, "BTC")
text = capsys.readouterr().out
assert re.search(
r".* Exit Reason .* Exits .* Avg Profit % .* Tot Profit BTC .* Tot Profit % .* "
r"Avg Duration .* Win Draw Loss Win% .*",
text,
)
assert re.search(
2024-07-09 04:49:33 +00:00
r".* roi .* 2 .* 15.0 .* 0.60000000 .* 2.73 .* 0:20:00 .* 2 0 0 100 .*",
text,
)
assert re.search(
2024-07-09 04:49:33 +00:00
r".* stop_loss .* 1 .* -10.0 .* -0.20000000 .* -0.91 .* 0:10:00 .* 0 0 1 0 .*",
text,
)
assert re.search(
r".* TOTAL .* 3 .* 6.67 .* 0.40000000 .* 1.82 .* 0:17:00 .* 2 0 1 66.7 .*", text
)
2020-09-26 12:55:12 +00:00
def test_generate_sell_reason_stats():
2020-05-25 05:14:21 +00:00
results = pd.DataFrame(
{
2024-05-12 14:04:01 +00:00
"pair": ["ETH/BTC", "ETH/BTC", "ETH/BTC"],
"profit_ratio": [0.1, 0.2, -0.1],
"profit_abs": [0.2, 0.4, -0.2],
"trade_duration": [10, 30, 10],
"wins": [2, 0, 0],
"draws": [0, 0, 0],
"losses": [0, 0, 1],
"exit_reason": [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value],
2020-05-25 05:14:21 +00:00
}
)
2024-05-12 14:04:01 +00:00
exit_reason_stats = generate_tag_metrics(
"exit_reason", starting_balance=22, results=results, skip_nan=False
)
2022-03-24 19:33:47 +00:00
roi_result = exit_reason_stats[0]
2024-05-12 14:04:01 +00:00
assert roi_result["key"] == "roi"
assert roi_result["trades"] == 2
assert pytest.approx(roi_result["profit_mean"]) == 0.15
assert roi_result["profit_mean_pct"] == round(roi_result["profit_mean"] * 100, 2)
assert pytest.approx(roi_result["profit_mean"]) == 0.15
assert roi_result["profit_mean_pct"] == round(roi_result["profit_mean"] * 100, 2)
2020-05-25 05:14:21 +00:00
2022-03-24 19:33:47 +00:00
stop_result = exit_reason_stats[1]
2020-05-25 05:14:21 +00:00
2024-05-12 14:04:01 +00:00
assert stop_result["key"] == "stop_loss"
assert stop_result["trades"] == 1
assert pytest.approx(stop_result["profit_mean"]) == -0.1
assert stop_result["profit_mean_pct"] == round(stop_result["profit_mean"] * 100, 2)
assert pytest.approx(stop_result["profit_mean"]) == -0.1
assert stop_result["profit_mean_pct"] == round(stop_result["profit_mean"] * 100, 2)
2020-05-25 05:14:21 +00:00
2024-07-08 05:07:50 +00:00
def test_text_table_strategy(testdatadir, capsys):
filename = testdatadir / "backtest_results/backtest-result_multistrat.json"
2022-01-05 19:29:40 +00:00
bt_res_data = load_backtest_stats(filename)
2024-05-12 14:04:01 +00:00
bt_res_data_comparison = bt_res_data.pop("strategy_comparison")
2024-05-12 14:04:01 +00:00
strategy_results = generate_strategy_comparison(bt_stats=bt_res_data["strategy"])
2022-01-05 19:29:40 +00:00
assert strategy_results == bt_res_data_comparison
2024-07-08 05:07:50 +00:00
text_table_strategy(strategy_results, "BTC", "STRATEGY SUMMARY")
2024-07-08 05:05:56 +00:00
2024-07-08 05:07:50 +00:00
captured = capsys.readouterr()
text = captured.out
2024-07-08 05:05:56 +00:00
assert re.search(
r".* Strategy .* Trades .* Avg Profit % .* Tot Profit BTC .* Tot Profit % .* "
r"Avg Duration .* Win Draw Loss Win% .* Drawdown .*",
text,
)
assert re.search(
r".*StrategyTestV2 .* 179 .* 0.08 .* 0.02608550 .* "
r"260.85 .* 3:40:00 .* 170 0 9 95.0 .* 0.00308222 BTC 8.67%.*",
text,
)
assert re.search(
r".*TestStrategy .* 179 .* 0.08 .* 0.02608550 .* "
r"260.85 .* 3:40:00 .* 170 0 9 95.0 .* 0.00308222 BTC 8.67%.*",
text,
)
2024-07-09 05:08:10 +00:00
def test_generate_edge_table(capsys):
results = {}
2024-05-12 14:04:01 +00:00
results["ETH/BTC"] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
2024-07-09 05:08:10 +00:00
generate_edge_table(results)
text = capsys.readouterr().out
assert re.search(r".* ETH/BTC .*", text)
2024-07-08 05:12:51 +00:00
assert re.search(r".* Risk Reward Ratio .* Required Risk Reward .* Expectancy .*", text)
2021-10-21 05:09:17 +00:00
def test_generate_periodic_breakdown_stats(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
2024-05-12 14:04:01 +00:00
bt_data = load_backtest_data(filename).to_dict(orient="records")
2021-10-21 05:09:17 +00:00
2024-05-12 14:04:01 +00:00
res = generate_periodic_breakdown_stats(bt_data, "day")
2021-10-21 05:09:17 +00:00
assert isinstance(res, list)
assert len(res) == 21
day = res[0]
2024-05-12 14:04:01 +00:00
assert "date" in day
assert "draws" in day
assert "loses" in day
assert "wins" in day
assert "profit_abs" in day
2021-10-21 05:09:17 +00:00
# Select empty dataframe!
2024-05-12 14:04:01 +00:00
res = generate_periodic_breakdown_stats([], "day")
2021-10-21 05:09:17 +00:00
assert res == []
def test__get_resample_from_period():
2024-05-12 14:04:01 +00:00
assert _get_resample_from_period("day") == "1d"
assert _get_resample_from_period("week") == "1W-MON"
assert _get_resample_from_period("month") == "1ME"
2021-10-21 05:09:17 +00:00
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
2024-05-12 14:04:01 +00:00
_get_resample_from_period("noooo")
2021-10-30 14:53:48 +00:00
2023-04-24 07:41:36 +00:00
for period in BACKTEST_BREAKDOWNS:
assert isinstance(_get_resample_from_period(period), str)
2021-10-30 14:53:48 +00:00
2021-10-30 15:05:12 +00:00
def test_show_sorted_pairlist(testdatadir, default_conf, capsys):
filename = testdatadir / "backtest_results/backtest-result.json"
2021-10-30 14:53:48 +00:00
bt_data = load_backtest_stats(filename)
2024-05-12 14:04:01 +00:00
default_conf["backtest_show_pair_list"] = True
2021-10-30 14:53:48 +00:00
2021-10-30 15:05:12 +00:00
show_sorted_pairlist(default_conf, bt_data)
2021-10-30 14:53:48 +00:00
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:01 +00:00
assert "Pairs for Strategy StrategyTestV3: \n[" in out
assert "TOTAL" not in out
2021-10-30 14:53:48 +00:00
assert '"ETH/BTC", // ' in out