2020-07-26 11:44:34 +00:00
|
|
|
<template>
|
2020-12-05 15:32:44 +00:00
|
|
|
<div class="container-fluid">
|
2021-01-06 12:13:02 +00:00
|
|
|
<h2 class="mt-3">Backtesting</h2>
|
2020-12-05 15:32:44 +00:00
|
|
|
<div class="container">
|
2021-01-06 12:13:02 +00:00
|
|
|
<div class="row mx-5 d-flex flex-wrap justify-content-center mb-4">
|
2020-12-05 15:32:44 +00:00
|
|
|
<b-form-radio
|
|
|
|
v-model="btFormMode"
|
|
|
|
name="bt-form-radios"
|
|
|
|
button
|
2021-01-06 12:13:02 +00:00
|
|
|
class="mx-1"
|
2020-12-05 15:32:44 +00:00
|
|
|
value="run"
|
|
|
|
:disabled="!canRunBacktest"
|
|
|
|
>Run backtest</b-form-radio
|
2020-09-25 04:22:34 +00:00
|
|
|
>
|
2020-12-05 15:32:44 +00:00
|
|
|
<b-form-radio
|
|
|
|
v-model="btFormMode"
|
|
|
|
name="bt-form-radios"
|
|
|
|
button
|
2021-01-06 12:13:02 +00:00
|
|
|
class="mx-1"
|
2020-12-05 15:32:44 +00:00
|
|
|
value="results"
|
|
|
|
:disabled="!hasBacktestResult"
|
|
|
|
>Analyze result</b-form-radio
|
|
|
|
>
|
|
|
|
<b-form-radio
|
|
|
|
v-model="btFormMode"
|
|
|
|
name="bt-form-radios"
|
|
|
|
button
|
2021-01-06 12:13:02 +00:00
|
|
|
class="mx-1"
|
2020-12-05 15:32:44 +00:00
|
|
|
value="visualize-summary"
|
|
|
|
:disabled="!hasBacktestResult"
|
|
|
|
>Visualize summary</b-form-radio
|
|
|
|
>
|
|
|
|
<b-form-radio
|
|
|
|
v-model="btFormMode"
|
|
|
|
name="bt-form-radios"
|
|
|
|
button
|
2021-01-06 12:13:02 +00:00
|
|
|
class="mx-1"
|
2020-12-05 15:32:44 +00:00
|
|
|
value="visualize"
|
|
|
|
:disabled="!hasBacktestResult"
|
|
|
|
>Visualize result</b-form-radio
|
|
|
|
>
|
|
|
|
</div>
|
2020-09-25 04:22:34 +00:00
|
|
|
|
2021-01-06 12:13:02 +00:00
|
|
|
<div v-if="btFormMode == 'run'" class="row d-flex flex-column">
|
2020-12-05 15:32:44 +00:00
|
|
|
<StrategyList v-model="strategy"></StrategyList>
|
2020-09-25 04:22:34 +00:00
|
|
|
|
2020-12-05 15:32:44 +00:00
|
|
|
<b-card bg-variant="light" class="w-60" :disabled="backtestRunning">
|
|
|
|
<!-- Backtesting parameters -->
|
2020-09-25 04:22:34 +00:00
|
|
|
<b-form-group
|
2020-12-05 15:32:44 +00:00
|
|
|
label-cols-lg="2"
|
|
|
|
label="Backtest params"
|
|
|
|
label-size="sm"
|
|
|
|
label-class="font-weight-bold pt-0"
|
|
|
|
class="mb-0"
|
2020-07-27 05:19:45 +00:00
|
|
|
>
|
2020-12-05 15:32:44 +00:00
|
|
|
<b-form-group
|
|
|
|
label-cols-sm="5"
|
|
|
|
label="Timeframe:"
|
|
|
|
label-align-sm="right"
|
|
|
|
label-for="timeframeSelect"
|
|
|
|
>
|
|
|
|
<b-form-select
|
|
|
|
id="timeframe-select"
|
2021-01-02 14:10:10 +00:00
|
|
|
v-model="selectedTimeframe"
|
2020-12-05 15:32:44 +00:00
|
|
|
placeholder="Use strategy default"
|
|
|
|
:options="availableTimeframes"
|
|
|
|
></b-form-select>
|
|
|
|
</b-form-group>
|
|
|
|
|
|
|
|
<b-form-group
|
|
|
|
label-cols-sm="5"
|
|
|
|
label="Max open trades:"
|
|
|
|
label-align-sm="right"
|
|
|
|
label-for="max-open-trades"
|
|
|
|
>
|
|
|
|
<b-form-input
|
|
|
|
id="max-open-trades"
|
2020-12-08 18:35:06 +00:00
|
|
|
v-model="maxOpenTrades"
|
2020-12-05 15:32:44 +00:00
|
|
|
placeholder="Use strategy default"
|
|
|
|
type="number"
|
|
|
|
></b-form-input>
|
|
|
|
</b-form-group>
|
|
|
|
|
|
|
|
<b-form-group
|
|
|
|
label-cols-sm="5"
|
|
|
|
label="Stake amount:"
|
|
|
|
label-align-sm="right"
|
|
|
|
label-for="stake-amount"
|
|
|
|
>
|
|
|
|
<b-form-input
|
|
|
|
id="stake-amount"
|
2020-12-08 18:35:06 +00:00
|
|
|
v-model="stakeAmount"
|
2020-12-05 15:32:44 +00:00
|
|
|
type="number"
|
|
|
|
placeholder="Use strategy default"
|
|
|
|
step="0.01"
|
|
|
|
></b-form-input>
|
|
|
|
</b-form-group>
|
|
|
|
|
2020-12-08 18:35:06 +00:00
|
|
|
<!-- <b-form-group label-cols-sm="5" label="Fee:" label-align-sm="right" label-for="fee">
|
2020-12-05 15:32:44 +00:00
|
|
|
<b-form-input
|
|
|
|
id="fee"
|
|
|
|
type="number"
|
|
|
|
placeholder="Use exchange default"
|
|
|
|
step="0.01"
|
|
|
|
></b-form-input>
|
2020-12-08 18:35:06 +00:00
|
|
|
</b-form-group> -->
|
2020-09-25 04:22:34 +00:00
|
|
|
</b-form-group>
|
2020-12-05 15:32:44 +00:00
|
|
|
</b-card>
|
|
|
|
|
2021-01-06 12:13:02 +00:00
|
|
|
<TimeRangeSelect v-model="timerange" class="mt-2"></TimeRangeSelect>
|
|
|
|
<h3 class="mt-3">Backtesting summary</h3>
|
|
|
|
<div class="d-flex justify-content-center">
|
|
|
|
<b-button
|
|
|
|
variant="primary"
|
|
|
|
:disabled="backtestRunning"
|
|
|
|
class="mx-1"
|
|
|
|
@click="clickBacktest"
|
|
|
|
>
|
2020-12-05 15:32:44 +00:00
|
|
|
Start backtest
|
|
|
|
</b-button>
|
2021-01-06 12:13:02 +00:00
|
|
|
<b-button
|
|
|
|
variant="primary"
|
|
|
|
:disabled="backtestRunning"
|
|
|
|
class="mx-1"
|
|
|
|
@click="pollBacktest"
|
|
|
|
>
|
2020-12-05 15:32:44 +00:00
|
|
|
Load backtest result
|
|
|
|
</b-button>
|
2021-01-06 12:13:02 +00:00
|
|
|
<b-button variant="primary" class="mx-1" @click="removeBacktest">Reset Backtest</b-button>
|
2020-12-05 15:32:44 +00:00
|
|
|
</div>
|
2020-09-25 04:22:34 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="hasBacktestResult && btFormMode == 'results'" class="text-center w-100 mt-2">
|
|
|
|
<BacktestResultView :strategy="strategy" :backtest-result="selectedBacktestResult" />
|
|
|
|
</div>
|
2020-11-01 12:58:16 +00:00
|
|
|
<div
|
|
|
|
v-if="hasBacktestResult && btFormMode == 'visualize-summary'"
|
|
|
|
class="text-center w-100 mt-2 cum-profit-container"
|
|
|
|
>
|
|
|
|
<CumProfitChart
|
|
|
|
:trades="selectedBacktestResult.trades"
|
|
|
|
profit-column="profit_abs"
|
|
|
|
:show-title="true"
|
|
|
|
/>
|
|
|
|
</div>
|
2020-12-05 16:42:22 +00:00
|
|
|
<div
|
|
|
|
v-if="hasBacktestResult && btFormMode == 'visualize'"
|
|
|
|
class="container-fluid row text-center w-100 mt-2"
|
|
|
|
>
|
|
|
|
<PairSummary
|
|
|
|
class="col-md-2"
|
|
|
|
:pairlist="selectedBacktestResult.pairlist"
|
|
|
|
:trades="selectedBacktestResult.trades"
|
2020-12-05 18:38:42 +00:00
|
|
|
sort-method="profit"
|
2020-12-05 16:42:22 +00:00
|
|
|
/>
|
2020-09-25 04:22:34 +00:00
|
|
|
<CandleChartContainer
|
|
|
|
:available-pairs="selectedBacktestResult.pairlist"
|
|
|
|
:historic-view="!!true"
|
|
|
|
:timeframe="timeframe"
|
|
|
|
:plot-config="selectedPlotConfig"
|
|
|
|
:timerange="timerange"
|
|
|
|
:strategy="strategy"
|
|
|
|
:trades="selectedBacktestResult.trades"
|
2020-12-05 16:42:22 +00:00
|
|
|
class="col-md-10 candle-chart-container"
|
2020-09-25 04:22:34 +00:00
|
|
|
>
|
|
|
|
</CandleChartContainer>
|
2020-07-26 17:35:56 +00:00
|
|
|
</div>
|
2020-07-26 11:44:34 +00:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
import { Component, Vue, Watch } from 'vue-property-decorator';
|
|
|
|
import { namespace } from 'vuex-class';
|
|
|
|
import TimeRangeSelect from '@/components/ftbot/TimeRangeSelect.vue';
|
2020-07-26 17:35:56 +00:00
|
|
|
import BacktestResultView from '@/components/ftbot/BacktestResultView.vue';
|
2020-09-12 17:21:35 +00:00
|
|
|
import CandleChartContainer from '@/components/charts/CandleChartContainer.vue';
|
2020-08-08 18:21:47 +00:00
|
|
|
import StrategyList from '@/components/ftbot/StrategyList.vue';
|
2020-11-01 12:47:57 +00:00
|
|
|
import ValuePair from '@/components/ftbot/ValuePair.vue';
|
2020-11-01 12:58:16 +00:00
|
|
|
import CumProfitChart from '@/components/charts/CumProfitChart.vue';
|
2020-12-05 16:42:22 +00:00
|
|
|
import PairSummary from '@/components/ftbot/PairSummary.vue';
|
2020-07-26 17:35:56 +00:00
|
|
|
|
2020-07-27 05:19:45 +00:00
|
|
|
import {
|
|
|
|
BacktestPayload,
|
|
|
|
BacktestResult,
|
|
|
|
PairHistoryPayload,
|
|
|
|
PlotConfig,
|
|
|
|
StrategyBacktestResult,
|
|
|
|
} from '@/types';
|
|
|
|
|
|
|
|
import { getCustomPlotConfig, getPlotConfigName } from '@/shared/storage';
|
2020-07-26 11:44:34 +00:00
|
|
|
|
|
|
|
const ftbot = namespace('ftbot');
|
|
|
|
@Component({
|
2020-11-01 12:47:57 +00:00
|
|
|
components: {
|
|
|
|
BacktestResultView,
|
|
|
|
TimeRangeSelect,
|
|
|
|
CandleChartContainer,
|
2020-11-01 12:58:16 +00:00
|
|
|
CumProfitChart,
|
2020-11-01 12:47:57 +00:00
|
|
|
StrategyList,
|
|
|
|
ValuePair,
|
2020-12-05 16:42:22 +00:00
|
|
|
PairSummary,
|
2020-11-01 12:47:57 +00:00
|
|
|
},
|
2020-07-26 11:44:34 +00:00
|
|
|
})
|
2020-07-26 17:35:56 +00:00
|
|
|
export default class Backtesting extends Vue {
|
|
|
|
pollInterval: number | null = null;
|
2020-07-26 11:44:34 +00:00
|
|
|
|
2020-09-25 04:22:34 +00:00
|
|
|
availableTimeframes = [
|
|
|
|
'1m',
|
|
|
|
'3m',
|
|
|
|
'5m',
|
|
|
|
'15m',
|
|
|
|
'30m',
|
|
|
|
'1h',
|
|
|
|
'2h',
|
|
|
|
'4h',
|
|
|
|
'6h',
|
|
|
|
'8h',
|
|
|
|
'12h',
|
|
|
|
'1d',
|
|
|
|
'3d',
|
|
|
|
'1w',
|
|
|
|
'2w',
|
|
|
|
'1M',
|
|
|
|
'1y',
|
|
|
|
];
|
|
|
|
|
2021-01-02 14:10:10 +00:00
|
|
|
selectedTimeframe = '';
|
|
|
|
|
2020-09-25 04:22:34 +00:00
|
|
|
strategy = '';
|
|
|
|
|
2020-12-05 15:32:44 +00:00
|
|
|
btFormMode = 'run';
|
2020-07-26 17:35:56 +00:00
|
|
|
|
2020-07-27 05:19:45 +00:00
|
|
|
selectedPlotConfig: PlotConfig = getCustomPlotConfig(getPlotConfigName());
|
2020-07-26 11:44:34 +00:00
|
|
|
|
|
|
|
@ftbot.State backtestRunning!: boolean;
|
|
|
|
|
2020-07-26 17:35:56 +00:00
|
|
|
@ftbot.State backtestResult!: BacktestResult;
|
|
|
|
|
2020-07-27 05:19:45 +00:00
|
|
|
@ftbot.State history;
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
@ftbot.Action public getPairHistory!: (payload: PairHistoryPayload) => void;
|
|
|
|
|
2020-07-26 11:44:34 +00:00
|
|
|
timerange = '';
|
|
|
|
|
2020-12-08 18:35:06 +00:00
|
|
|
maxOpenTrades = '';
|
|
|
|
|
|
|
|
stakeAmount = '';
|
|
|
|
|
2020-07-26 17:35:56 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
@ftbot.Action startBacktest!: (payload: BacktestPayload) => void;
|
2020-07-26 11:44:34 +00:00
|
|
|
|
|
|
|
@ftbot.Action pollBacktest!: () => void;
|
|
|
|
|
2020-09-18 18:56:20 +00:00
|
|
|
@ftbot.Action removeBacktest!: () => void;
|
|
|
|
|
2020-09-25 04:22:34 +00:00
|
|
|
get canRunBacktest() {
|
|
|
|
// TODO: Analyze if parameters and strategy has been selected.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-26 17:35:56 +00:00
|
|
|
get hasBacktestResult() {
|
|
|
|
return Object.keys(this.backtestResult).length !== 0;
|
|
|
|
}
|
|
|
|
|
2020-07-27 05:19:45 +00:00
|
|
|
get selectedBacktestResult(): StrategyBacktestResult {
|
|
|
|
return this.backtestResult.strategy[this.strategy] || {};
|
|
|
|
}
|
|
|
|
|
2020-08-08 18:21:47 +00:00
|
|
|
get timeframe(): string {
|
|
|
|
try {
|
|
|
|
return this.backtestResult.strategy[this.strategy].timeframe;
|
|
|
|
} catch (err) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-26 11:44:34 +00:00
|
|
|
clickBacktest() {
|
|
|
|
const btPayload: BacktestPayload = {
|
2020-07-26 17:35:56 +00:00
|
|
|
strategy: this.strategy,
|
2020-07-26 11:44:34 +00:00
|
|
|
timerange: this.timerange,
|
|
|
|
};
|
2021-01-06 12:13:10 +00:00
|
|
|
const openTradesInt = parseInt(this.maxOpenTrades, 10);
|
|
|
|
if (openTradesInt) {
|
2020-12-08 18:35:06 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
2021-01-06 12:13:10 +00:00
|
|
|
btPayload.max_open_trades = openTradesInt;
|
2020-12-08 18:35:06 +00:00
|
|
|
}
|
2021-01-06 12:13:10 +00:00
|
|
|
const stakeAmountInt = Number(this.stakeAmount);
|
|
|
|
if (stakeAmountInt) {
|
2020-12-08 18:35:06 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
2021-01-06 12:13:10 +00:00
|
|
|
btPayload.stake_amount = stakeAmountInt;
|
2020-12-08 18:35:06 +00:00
|
|
|
}
|
2021-01-02 14:10:10 +00:00
|
|
|
if (this.selectedTimeframe) {
|
|
|
|
btPayload.timeframe = this.selectedTimeframe;
|
|
|
|
}
|
2020-12-08 18:35:06 +00:00
|
|
|
|
2020-07-26 11:44:34 +00:00
|
|
|
this.startBacktest(btPayload);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Watch('backtestRunning')
|
|
|
|
backtestRunningChanged() {
|
|
|
|
if (this.backtestRunning === true) {
|
2020-07-26 17:35:56 +00:00
|
|
|
this.pollInterval = window.setInterval(this.pollBacktest, 1000);
|
2020-07-26 11:44:34 +00:00
|
|
|
} else if (this.pollInterval) {
|
|
|
|
clearInterval(this.pollInterval);
|
|
|
|
this.pollInterval = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
2020-09-16 18:28:48 +00:00
|
|
|
<style scoped>
|
|
|
|
.candle-chart-container {
|
|
|
|
height: 640px !important;
|
|
|
|
}
|
2020-11-01 12:58:16 +00:00
|
|
|
.cum-profit-container {
|
|
|
|
height: 640px !important;
|
|
|
|
}
|
2020-09-16 18:28:48 +00:00
|
|
|
</style>
|