Skip to content

Commit a6509a9

Browse files
committedDec 24, 2020
allow to run multiple symbol backtests at once
1 parent 26a907b commit a6509a9

File tree

5 files changed

+133
-30
lines changed

5 files changed

+133
-30
lines changed
 

‎src/modules/backtest.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ module.exports = class Backtest {
2626
};
2727
});
2828

29-
return Promise.all(asyncs.map(fn => fn()));
29+
const promise = await Promise.all(asyncs.map(fn => fn()));
30+
return promise.sort((a, b) => {
31+
const x = a.name;
32+
const y = b.name;
33+
return x < y ? -1 : x > y ? 1 : 0;
34+
});
3035
}
3136

3237
getBacktestStrategies() {

‎src/modules/http.js

+37-14
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,44 @@ module.exports = class Http {
130130
});
131131

132132
app.post('/backtest/submit', async (req, res) => {
133-
const pair = req.body.pair.split('.');
133+
let pairs = req.body.pair;
134134

135-
res.render(
136-
'../templates/backtest_submit.html.twig',
137-
await this.backtest.getBacktestResult(
138-
parseInt(req.body.ticker_interval),
139-
req.body.hours,
140-
req.body.strategy,
141-
req.body.candle_period,
142-
pair[0],
143-
pair[1],
144-
req.body.options ? JSON.parse(req.body.options) : {},
145-
req.body.initial_capital
146-
)
147-
);
135+
if (typeof pairs === 'string') {
136+
pairs = [pairs];
137+
}
138+
139+
const asyncs = pairs.map(pair => {
140+
return async () => {
141+
const p = pair.split('.');
142+
143+
return {
144+
pair: pair,
145+
result: await this.backtest.getBacktestResult(
146+
parseInt(req.body.ticker_interval),
147+
req.body.hours,
148+
req.body.strategy,
149+
req.body.candle_period,
150+
p[0],
151+
p[1],
152+
req.body.options ? JSON.parse(req.body.options) : {},
153+
req.body.initial_capital
154+
)
155+
};
156+
};
157+
});
158+
159+
const backtests = await Promise.all(asyncs.map(fn => fn()));
160+
161+
// single details view
162+
if (backtests.length === 1) {
163+
res.render('../templates/backtest_submit.html.twig', backtests[0].result);
164+
return;
165+
}
166+
167+
// multiple view
168+
res.render('../templates/backtest_submit_multiple.html.twig', {
169+
backtests: backtests
170+
});
148171
});
149172

150173
app.get('/tradingview/:symbol', (req, res) => {

‎templates/backtest.html.twig

+16-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<div class="content-wrapper">
88
<!-- Content Header (Page header) -->
99
<section class="content-header">
10-
<div class="container-fluid">
10+
<div class="container">
1111
<div class="row mb-2">
1212
<div class="col-sm-6">
1313
<h1>Backtesting</h1>
@@ -25,7 +25,7 @@
2525

2626
<!-- Main content -->
2727
<div class="content">
28-
<div class="container-fluid">
28+
<div class="container">
2929
<div class="row">
3030
<div class="col-md-12">
3131
<div class="card">
@@ -38,16 +38,17 @@
3838
<div class="col-sm">
3939
<div class="form-group">
4040
<label for="form-pair">Pair</label>
41-
<select class="form-control" id="form-pair" name="pair" required>
41+
<select class="form-control chosen-select" id="form-pair" name="pair" data-placeholder="Select one or multiple symbols" required multiple>
4242
{% for pair in pairs %}
4343
<option data-options="{% if pair.options %}{{ pair.options|format_json|escape('html') }}{% endif %}">{{ pair.name }}</option>
4444
{% endfor %}
4545
</select>
46+
<small class="form-text text-muted">Select one for detail view or multiple for a profit overview</small>
4647
</div>
4748

4849
<div class="form-group">
4950
<label for="form-strategies">Strategy</label>
50-
<select class="form-control" id="form-strategies" name="strategy" required>
51+
<select class="form-control chosen-select" id="form-strategies" name="strategy" required>
5152
<option disabled selected value> -- select an option -- </option>
5253
{% for strategy in strategies %}
5354
<option data-options="{% if strategy.options %}{{ strategy.options|format_json|escape('html') }}{% endif %}">{{ strategy.name }}</option>
@@ -64,28 +65,34 @@
6465
{% endfor %}
6566
{% endif %}
6667
</select>
68+
69+
<small class="form-text text-muted">Candle frequency of the chart on result page</small>
6770
</div>
6871

6972
<div class="form-group">
7073
<label for="form-hours">Last Hours</label>
7174
<input class="form-control" id="form-hours" name="hours" value="168" required>
75+
<small class="form-text text-muted">Starting window until now</small>
7276
</div>
7377
</div>
7478

7579
<div class="col-sm">
7680
<div class="form-group">
7781
<label for="form-initial-capital">Initial Capital </label>
7882
<input class="form-control" id="form-initial-capital" name="initial_capital" value="10000" required>
83+
<small class="form-text text-muted">Starting capital for calculating trades and their profit and losses</small>
7984
</div>
8085

8186
<div class="form-group">
8287
<label for="form-tick-interval">Signal Tick Interval (in minutes)</label>
8388
<input class="form-control" id="form-tick-interval" name="ticker_interval" value="15" required>
89+
<small class="form-text text-muted">Interval in which the strategy is mainly run</small>
8490
</div>
8591

8692
<div class="form-group">
8793
<label for="form-options">Strategy Options</label>
8894
<textarea class="form-control" id="form-options" rows="6" name="options"></textarea>
95+
<small class="form-text text-muted">Options provided by the strategy itself</small>
8996
</div>
9097
</div>
9198
</div>
@@ -111,4 +118,9 @@
111118

112119
{% block javascript %}
113120
<script src="/js/backtest-form.js?v={{ asset_version() }}"></script>
121+
<script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.min.js" integrity="sha512-rMGGF4wg1R73ehtnxXBt5mbUfN9JUJwbk21KMlnLZDJh7BkPmeovBuddZCENJddHYYMkCh9hPFnPmS9sspki8g==" crossorigin="anonymous"></script>
114122
{% endblock %}
123+
124+
{% block stylesheet %}
125+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.min.css" integrity="sha512-yVvxUQV0QESBt1SyZbNJMAwyKvFTLMyXSyBHDO4BG5t7k/Lw34tyqlSDlKIrIENIzCl+RVUNjmCPG+V/GMesRw==" crossorigin="anonymous" />
126+
{% endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{% extends './layout.html.twig' %}
2+
3+
{% block title %}Backtesting Results | Crypto Bot{% endblock %}
4+
5+
{% block stylesheet %}
6+
<link rel="stylesheet" href="/css/backtest.css?v={{ asset_version() }}">
7+
{% endblock %}
8+
9+
{% block content %}
10+
<!-- Content Wrapper. Contains page content -->
11+
<div class="content-wrapper">
12+
<!-- Content Header (Page header) -->
13+
<section class="content-header">
14+
<div class="container">
15+
<div class="row mb-2">
16+
<div class="col-sm-6">
17+
<h1>Backtesting Results</h1>
18+
</div>
19+
<div class="col-sm-6">
20+
<ol class="breadcrumb float-sm-right">
21+
<li class="breadcrumb-item"><a href="{{ '/' }}">Dashboard</a></li>
22+
<li class="breadcrumb-item active">Backtesting Results</li>
23+
</ol>
24+
</div>
25+
</div>
26+
</div><!-- /.container-fluid -->
27+
</section>
28+
<!-- /.Content Header (Page header) -->
29+
30+
<!-- Main content -->
31+
<div class="content">
32+
<div class="container">
33+
<div class="row">
34+
{% for backtest in backtests %}
35+
<div class="col-md-12">
36+
<div class="card">
37+
<div class="card-header">
38+
<h3 class="card-title">{{ backtest.pair }}</h3>
39+
</div>
40+
<!-- /.card-header -->
41+
<div class="card-body">
42+
{% include 'components/backtest_summary.html.twig' with {
43+
'summary': backtest.result.summary
44+
} only %}
45+
</div>
46+
</div>
47+
</div>
48+
{% endfor %}
49+
</div>
50+
<!-- /.row -->
51+
</div><!-- /.container-fluid -->
52+
</div>
53+
<!-- /.content -->
54+
</div>
55+
<!-- /.content-wrapper -->
56+
{% endblock %}
57+
58+
{% block javascript %}
59+
<script src="https://d3js.org/d3.v4.min.js"></script>
60+
<script src="https://cdnjs.cloudflare.com/ajax/libs/techan.js/0.8.0/techan.min.js"></script>
61+
<script src="/js/backtest.js?v={{ asset_version() }}"></script>
62+
{% endblock %}

‎web/static/js/backtest-form.js

+12-11
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,24 @@ $(function() {
1212
.val(options);
1313
}
1414
});
15+
16+
$('.chosen-select').chosen();
17+
1518
$('form.backtest-form #form-pair').change(function() {
1619
// get data as string
1720
const options = $(this)
1821
.find('option:selected')
1922
.attr('data-options');
2023

2124
if (options) {
22-
const optionTag = $(this)
23-
.closest('form')
24-
.find('#form-candle-period');
25-
26-
optionTag.html('');
27-
$.each(JSON.parse(options), function(key, value) {
28-
optionTag
29-
.append($('<option>', { value : value })
30-
.text(value));
31-
});
25+
const optionTag = $(this)
26+
.closest('form')
27+
.find('#form-candle-period');
28+
29+
optionTag.html('');
30+
$.each(JSON.parse(options), function(key, value) {
31+
optionTag.append($('<option>', { value: value }).text(value));
32+
});
3233
}
33-
});
34+
});
3435
});

0 commit comments

Comments
 (0)
Please sign in to comment.