|
1 | 1 | import json |
2 | 2 | import logging |
| 3 | +import shutil |
3 | 4 | import subprocess |
4 | 5 | from types import SimpleNamespace |
5 | 6 |
|
@@ -52,53 +53,63 @@ def __str__(self): |
52 | 53 | return message + "\n" |
53 | 54 |
|
54 | 55 |
|
55 | | -def exchange_backtest( |
56 | | - exchange, |
57 | | - tmp_path, |
58 | | - start_date, |
59 | | - end_date, |
60 | | - pairlist=None, |
61 | | - max_open_trades=5, |
62 | | - stake_amount="unlimited", |
63 | | -): |
64 | | - exchange_config = f"user_data/{exchange}-usdt-static.json" |
65 | | - json_results_file = tmp_path / "backtest-results.json" |
66 | | - cmdline = [ |
67 | | - "freqtrade", |
68 | | - "backtesting", |
69 | | - f"--user-data=user_data", |
70 | | - "--strategy-list=NostalgiaForInfinityNext", |
71 | | - f"--timerange={start_date}-{end_date}", |
72 | | - f"--max-open-trades={max_open_trades}", |
73 | | - f"--stake-amount={stake_amount}", |
74 | | - "--config=user_data/pairlists.json", |
75 | | - f"--export-filename={json_results_file}", |
76 | | - ] |
77 | | - if pairlist is None: |
78 | | - cmdline.append(f"--config={exchange_config}") |
79 | | - else: |
80 | | - pairlist_config = {"exchange": {"name": exchange, "pair_whitelist": pairlist}} |
81 | | - pairlist_config_file = tmp_path / "test-pairlist.json" |
82 | | - pairlist_config_file.write(json.dumps(pairlist_config)) |
83 | | - cmdline.append(f"--config={pairlist_config_file}") |
84 | | - log.info("Running cmdline '%s' on '%s'", " ".join(cmdline), REPO_ROOT) |
85 | | - proc = subprocess.run( |
86 | | - cmdline, check=False, shell=False, cwd=REPO_ROOT, text=True, capture_output=True |
87 | | - ) |
88 | | - ret = ProcessResult( |
89 | | - exitcode=proc.returncode, |
90 | | - stdout=proc.stdout.strip(), |
91 | | - stderr=proc.stderr.strip(), |
92 | | - cmdline=cmdline, |
93 | | - ) |
94 | | - log.info("Command Result:\n%s", ret) |
95 | | - assert ret.exitcode == 0 |
96 | | - generated_results_file = list(tmp_path.rglob("backtest-results-*.json"))[0] |
97 | | - results_data = json.loads(generated_results_file.read_text()) |
98 | | - data = { |
99 | | - "stdout": ret.stdout.strip(), |
100 | | - "stderr": ret.stderr.strip(), |
101 | | - "comparison": results_data["strategy_comparison"], |
102 | | - "results": results_data["strategy"]["NostalgiaForInfinityNext"], |
103 | | - } |
104 | | - return json.loads(json.dumps(data), object_hook=lambda d: SimpleNamespace(**d)) |
| 56 | +class Backtest: |
| 57 | + def __init__(self, request, exchange): |
| 58 | + self.request = request |
| 59 | + self.exchange = exchange |
| 60 | + |
| 61 | + def __call__( |
| 62 | + self, start_date, end_date, pairlist=None, max_open_trades=5, stake_amount="unlimited" |
| 63 | + ): |
| 64 | + tmp_path = self.request.getfixturevalue("tmp_path") |
| 65 | + exchange_config = f"user_data/{self.exchange}-usdt-static.json" |
| 66 | + json_results_file = tmp_path / "backtest-results.json" |
| 67 | + cmdline = [ |
| 68 | + "freqtrade", |
| 69 | + "backtesting", |
| 70 | + f"--user-data=user_data", |
| 71 | + "--strategy-list=NostalgiaForInfinityNext", |
| 72 | + f"--timerange={start_date}-{end_date}", |
| 73 | + f"--max-open-trades={max_open_trades}", |
| 74 | + f"--stake-amount={stake_amount}", |
| 75 | + "--config=user_data/pairlists.json", |
| 76 | + f"--export-filename={json_results_file}", |
| 77 | + ] |
| 78 | + if pairlist is None: |
| 79 | + cmdline.append(f"--config={exchange_config}") |
| 80 | + else: |
| 81 | + pairlist_config = {"exchange": {"name": self.exchange, "pair_whitelist": pairlist}} |
| 82 | + pairlist_config_file = tmp_path / "test-pairlist.json" |
| 83 | + pairlist_config_file.write(json.dumps(pairlist_config)) |
| 84 | + cmdline.append(f"--config={pairlist_config_file}") |
| 85 | + log.info("Running cmdline '%s' on '%s'", " ".join(cmdline), REPO_ROOT) |
| 86 | + proc = subprocess.run( |
| 87 | + cmdline, check=False, shell=False, cwd=REPO_ROOT, text=True, capture_output=True |
| 88 | + ) |
| 89 | + ret = ProcessResult( |
| 90 | + exitcode=proc.returncode, |
| 91 | + stdout=proc.stdout.strip(), |
| 92 | + stderr=proc.stderr.strip(), |
| 93 | + cmdline=cmdline, |
| 94 | + ) |
| 95 | + log.info("Command Result:\n%s", ret) |
| 96 | + assert ret.exitcode == 0 |
| 97 | + generated_results_file = list(tmp_path.rglob("backtest-results-*.json"))[0] |
| 98 | + if self.request.config.option.artifacts_path: |
| 99 | + generated_json_results_artifact_path = ( |
| 100 | + self.request.config.option.artifacts_path / generated_results_file.name |
| 101 | + ) |
| 102 | + shutil.copyfile(generated_results_file, generated_json_results_artifact_path) |
| 103 | + generated_txt_results_artifact_path = generated_json_results_artifact_path.with_suffix( |
| 104 | + ".txt" |
| 105 | + ) |
| 106 | + generated_txt_results_artifact_path.write_text(ret.stdout.strip()) |
| 107 | + |
| 108 | + results_data = json.loads(generated_results_file.read_text()) |
| 109 | + data = { |
| 110 | + "stdout": ret.stdout.strip(), |
| 111 | + "stderr": ret.stderr.strip(), |
| 112 | + "comparison": results_data["strategy_comparison"], |
| 113 | + "results": results_data["strategy"]["NostalgiaForInfinityNext"], |
| 114 | + } |
| 115 | + return json.loads(json.dumps(data), object_hook=lambda d: SimpleNamespace(**d)) |
0 commit comments