ruff format: update more tests

This commit is contained in:
Matthias
2024-05-12 16:04:01 +02:00
parent 40e161a5b9
commit 02075b15e3
4 changed files with 524 additions and 499 deletions

View File

@@ -51,7 +51,7 @@ def _backup_file(file: Path, copy_file: bool = False) -> None:
:param copy_file: keep file in place too.
:return: None
"""
file_swp = str(file) + '.swp'
file_swp = str(file) + ".swp"
if file.is_file():
file.rename(file_swp)
@@ -60,149 +60,172 @@ def _backup_file(file: Path, copy_file: bool = False) -> None:
def test_text_table_bt_results():
results = pd.DataFrame(
{
'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],
"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],
}
)
result_str = (
'| Pair | Entries | Avg Profit % | Tot Profit BTC | '
'Tot Profit % | Avg Duration | Win Draw Loss Win% |\n'
'|---------+-----------+----------------+------------------+'
'----------------+----------------+-------------------------|\n'
'| ETH/BTC | 3 | 8.33 | 0.50000000 | '
'12.50 | 0:20:00 | 2 0 1 66.7 |\n'
'| TOTAL | 3 | 8.33 | 0.50000000 | '
'12.50 | 0:20:00 | 2 0 1 66.7 |'
"| Pair | Entries | Avg Profit % | Tot Profit BTC | "
"Tot Profit % | Avg Duration | Win Draw Loss Win% |\n"
"|---------+-----------+----------------+------------------+"
"----------------+----------------+-------------------------|\n"
"| ETH/BTC | 3 | 8.33 | 0.50000000 | "
"12.50 | 0:20:00 | 2 0 1 66.7 |\n"
"| TOTAL | 3 | 8.33 | 0.50000000 | "
"12.50 | 0:20:00 | 2 0 1 66.7 |"
)
pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC',
starting_balance=4, results=results)
assert text_table_bt_results(pair_results, stake_currency='BTC') == result_str
pair_results = generate_pair_metrics(
["ETH/BTC"], stake_currency="BTC", starting_balance=4, results=results
)
assert text_table_bt_results(pair_results, stake_currency="BTC") == result_str
def test_generate_backtest_stats(default_conf, testdatadir, tmp_path):
default_conf.update({'strategy': CURRENT_TEST_STRATEGY})
default_conf.update({"strategy": CURRENT_TEST_STRATEGY})
StrategyResolver.load_strategy(default_conf)
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, ExitType.STOP_LOSS,
ExitType.ROI, ExitType.FORCE_EXIT]
}),
'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',
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,
ExitType.STOP_LOSS,
ExitType.ROI,
ExitType.FORCE_EXIT,
],
}
),
"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",
}
}
timerange = TimeRange.parse_timerange('1510688220-1510700340')
}
timerange = TimeRange.parse_timerange("1510688220-1510700340")
min_date = dt_from_ts(1510688220)
max_date = dt_from_ts(1510700340)
btdata = history.load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange,
fill_up_missing=True)
btdata = history.load_data(
testdatadir, "1m", ["UNITTEST/BTC"], timerange=timerange, fill_up_missing=True
)
stats = generate_backtest_stats(btdata, results, min_date, max_date)
assert isinstance(stats, dict)
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'])
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"])
# Above sample had no losing trade
assert strat_stats['max_drawdown_account'] == 0.0
assert strat_stats["max_drawdown_account"] == 0.0
# Retry with losing trade
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, ExitType.ROI,
ExitType.STOP_LOSS, ExitType.FORCE_EXIT]
}),
'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',
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,
ExitType.ROI,
ExitType.STOP_LOSS,
ExitType.FORCE_EXIT,
],
}
),
"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",
}
}
stats = generate_backtest_stats(btdata, results, min_date, max_date)
assert isinstance(stats, dict)
assert 'strategy' in stats
assert 'DefStrat' in stats['strategy']
assert 'strategy_comparison' in stats
strat_stats = stats['strategy']['DefStrat']
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']
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"]
# Test storing stats
filename = tmp_path / 'btresult.json'
filename = tmp_path / "btresult.json"
filename_last = tmp_path / LAST_BT_RESULT_FN
_backup_file(filename_last, copy_file=True)
assert not filename.is_file()
store_backtest_stats(filename, stats, '2022_01_01_15_05_13')
store_backtest_stats(filename, stats, "2022_01_01_15_05_13")
# get real Filename (it's btresult-<date>.json)
last_fn = get_latest_backtest_filename(filename_last.parent)
@@ -211,9 +234,9 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path):
filename1 = tmp_path / last_fn
assert filename1.is_file()
content = filename1.read_text()
assert 'max_drawdown_account' in content
assert 'strategy' in content
assert 'pairlist' in content
assert "max_drawdown_account" in content
assert "strategy" in content
assert "pairlist" in content
assert filename_last.is_file()
@@ -222,149 +245,146 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path):
def test_store_backtest_stats(testdatadir, mocker):
dump_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_json")
dump_mock = mocker.patch('freqtrade.optimize.optimize_reports.bt_storage.file_dump_json')
data = {'metadata': {}, 'strategy': {}, 'strategy_comparison': []}
store_backtest_stats(testdatadir, data, '2022_01_01_15_05_13')
data = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
store_backtest_stats(testdatadir, data, "2022_01_01_15_05_13")
assert dump_mock.call_count == 3
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / 'backtest-result'))
assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / "backtest-result"))
dump_mock.reset_mock()
filename = testdatadir / 'testresult.json'
store_backtest_stats(filename, data, '2022_01_01_15_05_13')
filename = testdatadir / "testresult.json"
store_backtest_stats(filename, data, "2022_01_01_15_05_13")
assert dump_mock.call_count == 3
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
# result will be testdatadir / testresult-<timestamp>.json
assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / 'testresult'))
assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / "testresult"))
def test_store_backtest_stats_real(tmp_path):
data = {'metadata': {}, 'strategy': {}, 'strategy_comparison': []}
store_backtest_stats(tmp_path, data, '2022_01_01_15_05_13')
data = {"metadata": {}, "strategy": {}, "strategy_comparison": []}
store_backtest_stats(tmp_path, data, "2022_01_01_15_05_13")
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()
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()
assert (tmp_path / LAST_BT_RESULT_FN).is_file()
fn = get_latest_backtest_filename(tmp_path)
assert fn == 'backtest-result-2022_01_01_15_05_13.json'
assert fn == "backtest-result-2022_01_01_15_05_13.json"
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()
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()
assert (tmp_path / LAST_BT_RESULT_FN).is_file()
# Last file reference should be updated
fn = get_latest_backtest_filename(tmp_path)
assert fn == 'backtest-result-2024_01_01_15_05_25.json'
assert fn == "backtest-result-2024_01_01_15_05_25.json"
def test_store_backtest_candles(testdatadir, mocker):
dump_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_joblib")
dump_mock = mocker.patch(
'freqtrade.optimize.optimize_reports.bt_storage.file_dump_joblib')
candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}}
candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}}
# mock directory exporting
store_backtest_analysis_results(testdatadir, candle_dict, {}, '2022_01_01_15_05_13')
store_backtest_analysis_results(testdatadir, candle_dict, {}, "2022_01_01_15_05_13")
assert dump_mock.call_count == 2
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
assert str(dump_mock.call_args_list[0][0][0]).endswith('_signals.pkl')
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
dump_mock.reset_mock()
# mock file exporting
filename = Path(testdatadir / 'testresult')
store_backtest_analysis_results(filename, candle_dict, {}, '2022_01_01_15_05_13')
filename = Path(testdatadir / "testresult")
store_backtest_analysis_results(filename, candle_dict, {}, "2022_01_01_15_05_13")
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
assert str(dump_mock.call_args_list[0][0][0]).endswith('_signals.pkl')
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
dump_mock.reset_mock()
def test_write_read_backtest_candles(tmp_path):
candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}}
candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}}
# test directory exporting
sample_date = '2022_01_01_15_05_13'
sample_date = "2022_01_01_15_05_13"
store_backtest_analysis_results(tmp_path, candle_dict, {}, sample_date)
stored_file = tmp_path / f'backtest-result-{sample_date}_signals.pkl'
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()
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'])
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
filename = tmp_path / 'testresult'
filename = tmp_path / "testresult"
store_backtest_analysis_results(filename, candle_dict, {}, sample_date)
stored_file = tmp_path / f'testresult-{sample_date}_signals.pkl'
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()
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'])
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)
def test_generate_pair_metrics():
results = pd.DataFrame(
{
'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]
"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],
}
)
pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC',
starting_balance=2, results=results)
pair_results = generate_pair_metrics(
["ETH/BTC"], stake_currency="BTC", starting_balance=2, results=results
)
assert isinstance(pair_results, list)
assert len(pair_results) == 2
assert pair_results[-1]['key'] == 'TOTAL'
assert pair_results[-1]["key"] == "TOTAL"
assert (
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)
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
def test_generate_daily_stats(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename)
res = generate_daily_stats(bt_data)
assert isinstance(res, dict)
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
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
# Select empty dataframe!
res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
res = generate_daily_stats(bt_data.loc[bt_data["open_date"] == "2000-01-01", :])
assert isinstance(res, dict)
assert round(res['backtest_best_day'], 4) == 0.0
assert res['winning_days'] == 0
assert res['draw_days'] == 0
assert res['losing_days'] == 0
assert round(res["backtest_best_day"], 4) == 0.0
assert res["winning_days"] == 0
assert res["draw_days"] == 0
assert res["losing_days"] == 0
def test_generate_trading_stats(testdatadir):
@@ -372,22 +392,24 @@ def test_generate_trading_stats(testdatadir):
bt_data = load_backtest_data(filename)
res = generate_trading_stats(bt_data)
assert isinstance(res, dict)
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
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!
res = generate_trading_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
assert res['wins'] == 0
assert res['losses'] == 0
res = generate_trading_stats(bt_data.loc[bt_data["open_date"] == "2000-01-01", :])
assert res["wins"] == 0
assert res["losses"] == 0
def test_calc_streak(testdatadir):
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],
})
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
res = calc_streak(df)
assert res == (4, 3)
@@ -396,12 +418,14 @@ def test_calc_streak(testdatadir):
# invert situation
df1 = df.copy()
df1['profit_ratio'] = df1['profit_ratio'] * -1
df1["profit_ratio"] = df1["profit_ratio"] * -1
assert calc_streak(df1) == (3, 4)
df_empty = pd.DataFrame({
'profit_ratio': [],
})
df_empty = pd.DataFrame(
{
"profit_ratio": [],
}
)
assert df_empty.empty
assert calc_streak(df_empty) == (0, 0)
@@ -411,131 +435,133 @@ def test_calc_streak(testdatadir):
def test_text_table_exit_reason():
results = pd.DataFrame(
{
'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, ExitType.ROI, ExitType.STOP_LOSS]
"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, ExitType.ROI, ExitType.STOP_LOSS],
}
)
result_str = (
'| Exit Reason | Exits | Avg Profit % | Tot Profit BTC | Tot Profit % |'
' Avg Duration | Win Draw Loss Win% |\n'
'|---------------+---------+----------------+------------------+----------------+'
'----------------+-------------------------|\n'
'| roi | 2 | 15.00 | 0.60000000 | 2.73 |'
' 0:20:00 | 2 0 0 100 |\n'
'| stop_loss | 1 | -10.00 | -0.20000000 | -0.91 |'
' 0:10:00 | 0 0 1 0 |\n'
'| TOTAL | 3 | 6.67 | 0.40000000 | 1.82 |'
' 0:17:00 | 2 0 1 66.7 |'
"| Exit Reason | Exits | Avg Profit % | Tot Profit BTC | Tot Profit % |"
" Avg Duration | Win Draw Loss Win% |\n"
"|---------------+---------+----------------+------------------+----------------+"
"----------------+-------------------------|\n"
"| roi | 2 | 15.00 | 0.60000000 | 2.73 |"
" 0:20:00 | 2 0 0 100 |\n"
"| stop_loss | 1 | -10.00 | -0.20000000 | -0.91 |"
" 0:10:00 | 0 0 1 0 |\n"
"| TOTAL | 3 | 6.67 | 0.40000000 | 1.82 |"
" 0:17:00 | 2 0 1 66.7 |"
)
exit_reason_stats = generate_tag_metrics('exit_reason', starting_balance=22,
results=results, skip_nan=False)
assert text_table_tags('exit_tag', exit_reason_stats, 'BTC') == result_str
exit_reason_stats = generate_tag_metrics(
"exit_reason", starting_balance=22, results=results, skip_nan=False
)
assert text_table_tags("exit_tag", exit_reason_stats, "BTC") == result_str
def test_generate_sell_reason_stats():
results = pd.DataFrame(
{
'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]
"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],
}
)
exit_reason_stats = generate_tag_metrics('exit_reason', starting_balance=22,
results=results, skip_nan=False)
exit_reason_stats = generate_tag_metrics(
"exit_reason", starting_balance=22, results=results, skip_nan=False
)
roi_result = exit_reason_stats[0]
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)
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)
stop_result = exit_reason_stats[1]
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)
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)
def test_text_table_strategy(testdatadir):
filename = testdatadir / "backtest_results/backtest-result_multistrat.json"
bt_res_data = load_backtest_stats(filename)
bt_res_data_comparison = bt_res_data.pop('strategy_comparison')
bt_res_data_comparison = bt_res_data.pop("strategy_comparison")
result_str = (
'| Strategy | Entries | Avg Profit % | Tot Profit BTC |'
' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n'
'|----------------+-----------+----------------+------------------+'
'----------------+----------------+-------------------------+-----------------------|\n'
'| StrategyTestV2 | 179 | 0.08 | 0.02608550 |'
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n'
'| TestStrategy | 179 | 0.08 | 0.02608550 |'
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |'
"| Strategy | Entries | Avg Profit % | Tot Profit BTC |"
" Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n"
"|----------------+-----------+----------------+------------------+"
"----------------+----------------+-------------------------+-----------------------|\n"
"| StrategyTestV2 | 179 | 0.08 | 0.02608550 |"
" 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n"
"| TestStrategy | 179 | 0.08 | 0.02608550 |"
" 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |"
)
strategy_results = generate_strategy_comparison(bt_stats=bt_res_data['strategy'])
strategy_results = generate_strategy_comparison(bt_stats=bt_res_data["strategy"])
assert strategy_results == bt_res_data_comparison
assert text_table_strategy(strategy_results, 'BTC') == result_str
assert text_table_strategy(strategy_results, "BTC") == result_str
def test_generate_edge_table():
results = {}
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
assert generate_edge_table(results).count('+') == 7
assert generate_edge_table(results).count('| ETH/BTC |') == 1
assert generate_edge_table(results).count(
'| Risk Reward Ratio | Required Risk Reward | Expectancy |') == 1
results["ETH/BTC"] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
assert generate_edge_table(results).count("+") == 7
assert generate_edge_table(results).count("| ETH/BTC |") == 1
assert (
generate_edge_table(results).count(
"| Risk Reward Ratio | Required Risk Reward | Expectancy |"
)
== 1
)
def test_generate_periodic_breakdown_stats(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename).to_dict(orient='records')
bt_data = load_backtest_data(filename).to_dict(orient="records")
res = generate_periodic_breakdown_stats(bt_data, 'day')
res = generate_periodic_breakdown_stats(bt_data, "day")
assert isinstance(res, list)
assert len(res) == 21
day = res[0]
assert 'date' in day
assert 'draws' in day
assert 'loses' in day
assert 'wins' in day
assert 'profit_abs' in day
assert "date" in day
assert "draws" in day
assert "loses" in day
assert "wins" in day
assert "profit_abs" in day
# Select empty dataframe!
res = generate_periodic_breakdown_stats([], 'day')
res = generate_periodic_breakdown_stats([], "day")
assert res == []
def test__get_resample_from_period():
assert _get_resample_from_period('day') == '1d'
assert _get_resample_from_period('week') == '1W-MON'
assert _get_resample_from_period('month') == '1ME'
assert _get_resample_from_period("day") == "1d"
assert _get_resample_from_period("week") == "1W-MON"
assert _get_resample_from_period("month") == "1ME"
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
_get_resample_from_period('noooo')
_get_resample_from_period("noooo")
for period in BACKTEST_BREAKDOWNS:
assert isinstance(_get_resample_from_period(period), str)
@@ -544,11 +570,11 @@ def test__get_resample_from_period():
def test_show_sorted_pairlist(testdatadir, default_conf, capsys):
filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_stats(filename)
default_conf['backtest_show_pair_list'] = True
default_conf["backtest_show_pair_list"] = True
show_sorted_pairlist(default_conf, bt_data)
out, _err = capsys.readouterr()
assert 'Pairs for Strategy StrategyTestV3: \n[' in out
assert 'TOTAL' not in out
assert "Pairs for Strategy StrategyTestV3: \n[" in out
assert "TOTAL" not in out
assert '"ETH/BTC", // ' in out