Skip to content

Commit 96c60c4

Browse files
committed
Allow providing a path for generated artifacts
1 parent 5feba3b commit 96c60c4

File tree

6 files changed

+89
-57
lines changed

6 files changed

+89
-57
lines changed

.github/workflows/tests.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,13 @@ jobs:
255255
run: docker-compose build tests
256256

257257
- name: Run Tests
258-
run: docker-compose run --rm tests
258+
run: |
259+
mkdir artifacts
260+
chmod 777 artifacts
261+
docker-compose run --rm tests
262+
263+
- name: Upload Artifacts
264+
uses: actions/upload-artifact@v2
265+
with:
266+
name: testrun-artifacts
267+
path: artifacts/

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,4 @@ user_data/hyperopts
141141
user_data/logs
142142
user_data/notebooks
143143
user_data/plot
144+
artifacts/

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ services:
99
volumes:
1010
- "./:/testing"
1111
command: >
12-
python -m pytest -ra -vv -s --log-cli-level=info tests/
12+
python -m pytest -ra -vv -s --log-cli-level=info --artifacts-path=artifacts/ tests/
1313
entrypoint: []
1414
working_dir: /testing
1515
backtesting:
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import functools
2-
31
import pytest
42

5-
from tests.backtests.helpers import exchange_backtest
3+
from tests.backtests.helpers import Backtest
64

75

86
@pytest.fixture
9-
def backtest(tmp_path):
10-
return functools.partial(exchange_backtest, "binance", tmp_path)
7+
def backtest(request):
8+
return Backtest(request, "binance")

tests/backtests/helpers.py

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import shutil
34
import subprocess
45
from types import SimpleNamespace
56

@@ -52,53 +53,63 @@ def __str__(self):
5253
return message + "\n"
5354

5455

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))

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,16 @@
44
# Make sure we can import the strategy module directly
55
REPO_ROOT = Path(__file__).parent.parent
66
sys.path.insert(0, str(REPO_ROOT))
7+
8+
9+
def pytest_addoption(parser):
10+
parser.addoption(
11+
"--artifacts-path", default=None, help="Path to write generated test artifacts"
12+
)
13+
14+
15+
def pytest_configure(config):
16+
if config.option.artifacts_path:
17+
config.option.artifacts_path = Path(config.option.artifacts_path).resolve()
18+
if not config.option.artifacts_path.is_dir():
19+
config.option.artifacts_path.mkdir()

0 commit comments

Comments
 (0)