Merge pull request #1550 from freqtrade/feat/backtest-comparison

Add backtest comparison view, allowing the comparison of multiple strategies
This commit is contained in:
Matthias 2023-11-01 18:00:26 +01:00 committed by GitHub
commit cb0cd62d72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 15 deletions

View File

@ -0,0 +1,63 @@
<template>
<div class="px-0 mw-100">
<div class="d-flex justify-content-center">
<h3>Backtest-result comparison</h3>
</div>
<!-- <div class="d-flex">
<div v-for="[key, result] in Object.entries(backtestResults)" :key="key" class="border m-1">
<BacktestResultSelectEntry :backtest-result="result" />
</div>
</div> -->
<div class="d-flex flex-column text-start ms-0 me-2 gap-2">
<div class="d-flex flex-column flex-xl-row">
<div class="px-0 px-xl-0 pt-2 pt-xl-0 ps-xl-1 flex-fill">
<b-table bordered :items="backtestResultStats" :fields="backtestResultFields">
<template
v-for="[key, result] in Object.entries(backtestResults)"
#[`head(${key})`]
:key="key"
>
<BacktestResultSelectEntry :backtest-result="result" />
</template>
</b-table>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { BacktestResultInMemory } from '@/types';
import { formatObjectForTable } from '@/shared/objectToTableItems';
import { computed } from 'vue';
import { generateBacktestMetricRows } from '@/shared/backtestMetrics';
import { TableField } from 'bootstrap-vue-next';
import BacktestResultSelectEntry from '@/components/ftbot/BacktestResultSelectEntry.vue';
const props = defineProps({
backtestResults: { required: true, type: Object as () => Record<string, BacktestResultInMemory> },
});
const backtestResultStats = computed(() => {
const values = {};
Object.entries(props.backtestResults).forEach(([key, result]) => {
const tmp = generateBacktestMetricRows(result.strategy);
values[key] = tmp;
});
console.log(values);
// return '';
return formatObjectForTable(values, 'metric');
});
const backtestResultFields = computed<TableField[]>(() => {
const res = [{ key: 'metric', label: 'Metric' }];
Object.entries(props.backtestResults).forEach(([key, value]) => {
res.push({ key, label: value.metadata.strategyName });
});
return res;
});
</script>
<style lang="scss" scoped></style>

View File

@ -56,6 +56,7 @@
<b-form-input <b-form-input
id="starting-capital" id="starting-capital"
v-model="btStore.startingCapital" v-model="btStore.startingCapital"
placeholder="Use config default"
type="number" type="number"
step="0.001" step="0.001"
></b-form-input> ></b-form-input>
@ -66,20 +67,19 @@
label-align-sm="right" label-align-sm="right"
label-for="stake-amount" label-for="stake-amount"
> >
<div class="d-flex"> <div class="d-flex align-items-center">
<b-form-checkbox <div style="flex-basis: 100%" class="d-flex">
id="stake-amount-bool" <b-form-checkbox id="stake-amount-bool" v-model="btStore.stakeAmountUnlimited"
v-model="btStore.stakeAmountUnlimited" >Unlimited stake</b-form-checkbox
class="col-md-6" >
>Unlimited stake</b-form-checkbox </div>
>
<b-form-input <b-form-input
id="stake-amount" id="stake-amount"
v-model="btStore.stakeAmount" v-model="btStore.stakeAmount"
type="number" type="number"
placeholder="Use strategy default" placeholder="Use strategy default"
step="0.01" step="0.01"
style="flex-basis: 100%"
:disabled="btStore.stakeAmountUnlimited" :disabled="btStore.stakeAmountUnlimited"
></b-form-input> ></b-form-input>
</div> </div>
@ -90,6 +90,7 @@
label="Enable Protections:" label="Enable Protections:"
label-align-sm="right" label-align-sm="right"
label-for="enable-protections" label-for="enable-protections"
class="align-items-center"
> >
<b-form-checkbox <b-form-checkbox
id="enable-protections" id="enable-protections"
@ -102,6 +103,7 @@
label="Cache Backtest results:" label="Cache Backtest results:"
label-align-sm="right" label-align-sm="right"
label-for="enable-cache" label-for="enable-cache"
class="align-items-center"
> >
<b-form-checkbox id="enable-cache" v-model="btStore.allowCache"></b-form-checkbox> <b-form-checkbox id="enable-cache" v-model="btStore.allowCache"></b-form-checkbox>
</b-form-group> </b-form-group>
@ -111,6 +113,7 @@
label="Enable FreqAI:" label="Enable FreqAI:"
label-align-sm="right" label-align-sm="right"
label-for="enable-freqai" label-for="enable-freqai"
class="align-items-center"
> >
<template #label> <template #label>
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<b-form-group v-if="backtestMode" label-for="trade-filter" class="mb-2"> <b-form-group v-if="backtestMode" label-for="trade-filter" class="mb-2 me-5">
<b-form-input id="trade-filter" v-model="filterText" type="text" placeholder="Filter" /> <b-form-input id="trade-filter" v-model="filterText" type="text" placeholder="Filter" />
</b-form-group> </b-form-group>
<b-list-group> <b-list-group>

View File

@ -34,7 +34,7 @@
</div> </div>
<b-button <b-button
size="sm" size="sm"
class="ms-auto" class="ms-auto mt-auto"
variant="outline-secondary" variant="outline-secondary"
@click="ordersVisible[i] = !ordersVisible[i]" @click="ordersVisible[i] = !ordersVisible[i]"
><i-mdi-chevron-right v-if="!ordersVisible[i]" width="24" height="24" /> ><i-mdi-chevron-right v-if="!ordersVisible[i]" width="24" height="24" />

View File

@ -18,7 +18,7 @@
class="mx-1 flex-samesize-items" class="mx-1 flex-samesize-items"
value="historicResults" value="historicResults"
:disabled="!botStore.activeBot.canRunBacktest" :disabled="!botStore.activeBot.canRunBacktest"
>Load Results</b-form-radio ><i-mdi-cloud-download class="me-2" />Load Results</b-form-radio
> >
<b-form-radio <b-form-radio
v-model="btFormMode" v-model="btFormMode"
@ -27,7 +27,7 @@
class="mx-1 flex-samesize-items" class="mx-1 flex-samesize-items"
value="run" value="run"
:disabled="!botStore.activeBot.canRunBacktest" :disabled="!botStore.activeBot.canRunBacktest"
>Run backtest</b-form-radio ><i-mdi-run-fast class="me-2" />Run backtest</b-form-radio
> >
<b-form-radio <b-form-radio
id="bt-analyze-btn" id="bt-analyze-btn"
@ -37,7 +37,17 @@
class="mx-1 flex-samesize-items" class="mx-1 flex-samesize-items"
value="results" value="results"
:disabled="!hasBacktestResult" :disabled="!hasBacktestResult"
>Analyze result</b-form-radio ><i-mdi-table-eye class="me-2" />Analyze result</b-form-radio
>
<b-form-radio
v-if="hasMultiBacktestResult"
v-model="btFormMode"
name="bt-form-radios"
button
class="mx-1 flex-samesize-items"
value="compare-results"
:disabled="!hasMultiBacktestResult"
><i-mdi-compare-horizontal class="me-2" />Compare results</b-form-radio
> >
<b-form-radio <b-form-radio
v-model="btFormMode" v-model="btFormMode"
@ -46,7 +56,7 @@
class="mx-1 flex-samesize-items" class="mx-1 flex-samesize-items"
value="visualize-summary" value="visualize-summary"
:disabled="!hasBacktestResult" :disabled="!hasBacktestResult"
>Visualize summary</b-form-radio ><i-mdi-chart-bell-curve-cumulative class="me-2" />Visualize summary</b-form-radio
> >
<b-form-radio <b-form-radio
v-model="btFormMode" v-model="btFormMode"
@ -55,7 +65,7 @@
class="mx-1 flex-samesize-items" class="mx-1 flex-samesize-items"
value="visualize" value="visualize"
:disabled="!hasBacktestResult" :disabled="!hasBacktestResult"
>Visualize result</b-form-radio ><i-mdi-chart-timeline-variant-shimmer class="me-2" />Visualize result</b-form-radio
> >
</div> </div>
<small v-show="botStore.activeBot.backtestRunning" class="text-end bt-running-label" <small v-show="botStore.activeBot.backtestRunning" class="text-end bt-running-label"
@ -113,6 +123,12 @@
class="flex-fill" class="flex-fill"
/> />
<BacktestResultComparison
v-if="hasBacktestResult && btFormMode === 'compare-results'"
:backtest-results="botStore.activeBot.backtestHistory"
class="flex-fill"
/>
<BacktestGraphs <BacktestGraphs
v-if="hasBacktestResult && btFormMode === 'visualize-summary'" v-if="hasBacktestResult && btFormMode === 'visualize-summary'"
:trades="botStore.activeBot.selectedBacktestResult.trades" :trades="botStore.activeBot.selectedBacktestResult.trades"
@ -141,6 +157,7 @@ import BacktestHistoryLoad from '@/components/ftbot/BacktestHistoryLoad.vue';
import BacktestResultChart from '@/components/ftbot/BacktestResultChart.vue'; import BacktestResultChart from '@/components/ftbot/BacktestResultChart.vue';
import BacktestResultSelect from '@/components/ftbot/BacktestResultSelect.vue'; import BacktestResultSelect from '@/components/ftbot/BacktestResultSelect.vue';
import BacktestResultAnalysis from '@/components/ftbot/BacktestResultAnalysis.vue'; import BacktestResultAnalysis from '@/components/ftbot/BacktestResultAnalysis.vue';
import BacktestResultComparison from '@/components/ftbot/BacktestResultComparison.vue';
import BacktestRun from '@/components/ftbot/BacktestRun.vue'; import BacktestRun from '@/components/ftbot/BacktestRun.vue';
import { formatPercent } from '@/shared/formatters'; import { formatPercent } from '@/shared/formatters';
@ -153,6 +170,7 @@ enum BtRunModes {
results = 'results', results = 'results',
visualize = 'visualize', visualize = 'visualize',
visualizesummary = 'visualize-summary', visualizesummary = 'visualize-summary',
compareresults = 'compare-results',
historicresults = 'historicResults', historicresults = 'historicResults',
} }
@ -164,6 +182,12 @@ const hasBacktestResult = computed(() =>
? Object.keys(botStore.activeBot.backtestHistory).length !== 0 ? Object.keys(botStore.activeBot.backtestHistory).length !== 0
: false, : false,
); );
const hasMultiBacktestResult = computed(() =>
botStore.activeBot.backtestHistory
? Object.keys(botStore.activeBot.backtestHistory).length > 1
: false,
);
const timeframe = computed((): string => { const timeframe = computed((): string => {
try { try {
return botStore.activeBot.selectedBacktestResult.timeframe; return botStore.activeBot.selectedBacktestResult.timeframe;