frequi_origin/src/views/Backtesting.vue

535 lines
17 KiB
Vue
Raw Normal View History

2020-07-26 11:44:34 +00:00
<template>
2021-07-04 17:57:19 +00:00
<div class="container-fluid" style="max-height: calc(100vh - 60px)">
2021-07-04 14:08:43 +00:00
<div class="container-fluid">
<div class="row mb-2"></div>
2022-04-19 05:05:34 +00:00
<p v-if="!botStore.activeBot.canRunBacktest">
Bot must be in webserver mode to enable Backtesting.
</p>
2021-07-04 14:08:43 +00:00
<div class="row w-100">
<h2 class="col-4 col-lg-3">Backtesting</h2>
<div
2021-12-01 05:40:25 +00:00
class="col-12 col-lg-order-last col-lg-6 mx-md-5 d-flex flex-wrap justify-content-md-center justify-content-between mb-4"
2020-12-05 15:32:44 +00:00
>
<b-form-radio
2022-04-19 05:05:34 +00:00
v-if="botStore.activeBot.botApiVersion >= 2.15"
v-model="btFormMode"
name="bt-form-radios"
button
class="mx-1 flex-samesize-items"
value="historicResults"
2022-04-19 05:05:34 +00:00
:disabled="!botStore.activeBot.canRunBacktest"
>Load Results</b-form-radio
>
2021-07-04 14:08:43 +00:00
<b-form-radio
v-model="btFormMode"
name="bt-form-radios"
button
class="mx-1 flex-samesize-items"
value="run"
2022-04-19 05:05:34 +00:00
:disabled="!botStore.activeBot.canRunBacktest"
2021-07-04 14:08:43 +00:00
>Run backtest</b-form-radio
>
<b-form-radio
2022-04-13 17:55:27 +00:00
id="bt-analyze-btn"
2021-07-04 14:08:43 +00:00
v-model="btFormMode"
name="bt-form-radios"
button
class="mx-1 flex-samesize-items"
value="results"
:disabled="!hasBacktestResult"
>Analyze result</b-form-radio
>
<b-form-radio
v-model="btFormMode"
name="bt-form-radios"
button
class="mx-1 flex-samesize-items"
value="visualize-summary"
:disabled="!hasBacktestResult"
>Visualize summary</b-form-radio
>
<b-form-radio
v-model="btFormMode"
name="bt-form-radios"
button
class="mx-1 flex-samesize-items"
value="visualize"
:disabled="!hasBacktestResult"
>Visualize result</b-form-radio
>
</div>
2022-04-19 05:05:34 +00:00
<small
v-show="botStore.activeBot.backtestRunning"
class="text-right bt-running-label col-8 col-lg-3"
>Backtest running: {{ botStore.activeBot.backtestStep }}
{{ formatPercent(botStore.activeBot.backtestProgress, 2) }}</small
2020-12-05 15:32:44 +00:00
>
</div>
2021-03-11 18:17:09 +00:00
</div>
2021-06-25 18:00:40 +00:00
2021-05-23 14:25:31 +00:00
<div class="d-md-flex">
2021-03-11 18:17:09 +00:00
<!-- Left bar -->
<div
2021-05-23 14:25:31 +00:00
:class="`${showLeftBar ? 'col-md-3' : ''} sticky-top sticky-offset mr-3 d-flex flex-column`"
2021-03-11 18:17:09 +00:00
>
<b-button
2021-06-25 18:00:40 +00:00
v-if="btFormMode !== 'visualize'"
2021-03-11 18:17:09 +00:00
class="align-self-start"
aria-label="Close"
size="sm"
@click="showLeftBar = !showLeftBar"
>{{ showLeftBar ? '&lt;' : '&gt;' }}</b-button
>
<transition name="fade" mode="in-out">
<BacktestResultSelect
v-if="btFormMode !== 'visualize' && showLeftBar"
2022-04-19 05:05:34 +00:00
:backtest-history="botStore.activeBot.backtestHistory"
:selected-backtest-result-key="botStore.activeBot.selectedBacktestResultKey"
2022-09-14 18:44:29 +00:00
@selectionChange="botStore.activeBot.setBacktestResultKey"
/>
</transition>
2021-03-11 18:17:09 +00:00
</div>
<!-- End Left bar -->
<div
v-if="btFormMode == 'historicResults'"
class="flex-fill row d-flex flex-column bt-config"
>
<backtest-history-load />
</div>
2021-07-06 04:56:41 +00:00
<div v-if="btFormMode == 'run'" class="flex-fill row d-flex flex-column bt-config">
2021-05-24 09:34:48 +00:00
<div class="mb-2">
<span>Strategy</span>
<StrategySelect v-model="strategy"></StrategySelect>
</div>
2022-04-19 05:05:34 +00:00
<b-card bg-variant="light" :disabled="botStore.activeBot.backtestRunning">
2020-12-05 15:32:44 +00:00
<!-- 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
>
<b-form-group
label-cols-sm="5"
label="Timeframe:"
label-align-sm="right"
label-for="timeframe-select"
>
<TimeframeSelect id="timeframe-select" v-model="selectedTimeframe" />
</b-form-group>
<b-form-group
label-cols-sm="5"
label="Detail Timeframe:"
label-align-sm="right"
label-for="timeframe-detail-select"
title="Detail timeframe, to simulate intra-candle results. Not setting this will not use this functionality."
>
<TimeframeSelect
id="timeframe-detail-select"
v-model="selectedDetailTimeframe"
:below-timeframe="selectedTimeframe"
/>
</b-form-group>
2020-12-05 15:32:44 +00:00
<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="Starting capital:"
label-align-sm="right"
label-for="starting-capital"
>
<b-form-input
id="starting-capital"
v-model="startingCapital"
type="number"
step="0.001"
></b-form-input>
</b-form-group>
2020-12-05 15:32:44 +00:00
<b-form-group
label-cols-sm="5"
label="Stake amount:"
label-align-sm="right"
label-for="stake-amount"
>
2021-02-28 08:53:51 +00:00
<div class="d-flex">
2021-03-10 15:09:55 +00:00
<b-form-checkbox
id="stake-amount-bool"
v-model="stakeAmountUnlimited"
class="col-md-6"
2021-02-28 08:53:51 +00:00
>Unlimited stake</b-form-checkbox
>
<b-form-input
id="stake-amount"
v-model="stakeAmount"
type="number"
placeholder="Use strategy default"
step="0.01"
:disabled="stakeAmountUnlimited"
></b-form-input>
</div>
2020-12-05 15:32:44 +00:00
</b-form-group>
2021-01-06 19:25:38 +00:00
<b-form-group
label-cols-sm="5"
label="Enable Protections:"
label-align-sm="right"
label-for="enable-protections"
>
<b-form-checkbox
id="enable-protections"
v-model="enableProtections"
></b-form-checkbox>
</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> -->
<hr />
<TimeRangeSelect v-model="timerange" class="mt-2"></TimeRangeSelect>
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
<h3 class="mt-3">Backtesting summary</h3>
2021-05-24 09:34:48 +00:00
<div
class="d-flex flex-wrap flex-md-nowrap justify-content-between justify-content-md-center"
>
2021-01-06 12:13:02 +00:00
<b-button
2022-04-12 18:52:48 +00:00
id="start-backtest"
2021-01-06 12:13:02 +00:00
variant="primary"
2022-04-19 05:05:34 +00:00
:disabled="botStore.activeBot.backtestRunning || !botStore.activeBot.canRunBacktest"
2021-01-06 12:13:02 +00:00
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"
2022-04-19 05:05:34 +00:00
:disabled="botStore.activeBot.backtestRunning || !botStore.activeBot.canRunBacktest"
2021-01-06 12:13:02 +00:00
class="mx-1"
2022-04-19 05:05:34 +00:00
@click="botStore.activeBot.pollBacktest"
2021-01-06 12:13:02 +00:00
>
2020-12-05 15:32:44 +00:00
Load backtest result
</b-button>
2021-04-05 17:59:14 +00:00
<b-button
variant="primary"
class="mx-1"
2022-04-19 05:05:34 +00:00
:disabled="!botStore.activeBot.backtestRunning"
@click="botStore.activeBot.stopBacktest"
2021-04-24 14:51:28 +00:00
>Stop Backtest</b-button
2021-04-05 17:59:14 +00:00
>
<b-button
variant="primary"
class="mx-1"
2022-04-19 05:05:34 +00:00
:disabled="botStore.activeBot.backtestRunning || !botStore.activeBot.canRunBacktest"
@click="botStore.activeBot.removeBacktest"
>Reset Backtest</b-button
>
2020-12-05 15:32:44 +00:00
</div>
2020-09-25 04:22:34 +00:00
</div>
2021-03-11 18:17:09 +00:00
<BacktestResultView
v-if="hasBacktestResult && btFormMode == 'results'"
2022-04-19 05:05:34 +00:00
:backtest-result="botStore.activeBot.selectedBacktestResult"
2021-03-11 18:17:09 +00:00
class="flex-fill"
2020-11-01 12:58:16 +00:00
/>
2021-03-11 18:17:09 +00:00
<BacktestGraphsView
2021-03-11 18:17:09 +00:00
v-if="hasBacktestResult && btFormMode == 'visualize-summary'"
:trades="botStore.activeBot.selectedBacktestResult.trades"
/>
2020-11-01 12:58:16 +00:00
</div>
2021-03-11 18:17:09 +00:00
2020-12-05 16:42:22 +00:00
<div
v-if="hasBacktestResult && btFormMode == 'visualize'"
2021-06-25 18:00:40 +00:00
class="container-fluid text-center w-100 mt-2"
2020-12-05 16:42:22 +00:00
>
2022-08-01 00:58:50 +00:00
<div class="row">
<div class="col-md-11 text-left">
<p>
Graph will always show the latest values for the selected strategy. Timerange:
{{ timerange }} - {{ strategy }}
</p>
</div>
2022-08-01 01:04:36 +00:00
<div class="col-md-1 text-right">
2022-08-01 00:58:50 +00:00
<b-button
v-if="btFormMode === 'visualize'"
aria-label="Close"
2022-08-02 18:36:47 +00:00
title="Trade Navigation"
2022-08-01 00:58:50 +00:00
size="sm"
@click="showRightBar = !showRightBar"
2022-08-01 22:50:36 +00:00
>{{ showRightBar ? '&gt;' : '&lt;' }}
2022-08-01 00:58:50 +00:00
</b-button>
</div>
</div>
2021-06-25 18:00:40 +00:00
<div class="row text-center">
2021-01-15 19:41:32 +00:00
<PairSummary
2021-07-04 17:57:19 +00:00
class="col-md-2 overflow-auto"
style="max-height: calc(100vh - 200px)"
2022-04-19 05:05:34 +00:00
:pairlist="botStore.activeBot.selectedBacktestResult.pairlist"
:trades="botStore.activeBot.selectedBacktestResult.trades"
2021-01-15 19:41:32 +00:00
sort-method="profit"
2021-10-13 17:26:37 +00:00
:backtest-mode="true"
2021-01-15 19:41:32 +00:00
/>
<CandleChartContainer
2022-04-19 05:05:34 +00:00
:available-pairs="botStore.activeBot.selectedBacktestResult.pairlist"
2021-01-15 19:41:32 +00:00
:historic-view="!!true"
:timeframe="timeframe"
:timerange="timerange"
:strategy="strategy"
2022-04-19 05:05:34 +00:00
:trades="botStore.activeBot.selectedBacktestResult.trades"
2022-08-01 22:50:36 +00:00
:class="`${
showRightBar ? 'col-md-8' : 'col-md-10'
} candle-chart-container px-0 w-100 h-100`"
:slider-position="sliderPosition"
2021-01-15 19:41:32 +00:00
>
</CandleChartContainer>
2022-08-01 22:50:36 +00:00
<TradeListNav
v-if="showRightBar"
2022-08-01 00:58:50 +00:00
class="overflow-auto col-md-2"
2022-07-31 16:03:46 +00:00
style="max-height: calc(100vh - 200px)"
2022-08-01 22:50:36 +00:00
:trades="
botStore.activeBot.selectedBacktestResult.trades.filter(
(t) => t.pair === botStore.activeBot.selectedPair,
)
"
2022-08-01 00:58:50 +00:00
@trade-select="navigateChartToTrade"
2022-07-31 16:03:46 +00:00
/>
2021-01-15 19:41:32 +00:00
</div>
2022-04-08 05:44:28 +00:00
<b-card header="Single trades" class="row mt-2 w-100">
<TradeList
class="row trade-history mt-2 w-100"
2022-04-19 05:05:34 +00:00
:trades="botStore.activeBot.selectedBacktestResult.trades"
2022-04-08 05:44:28 +00:00
:show-filter="true"
2022-04-19 05:05:34 +00:00
:stake-currency="botStore.activeBot.selectedBacktestResult.stake_currency"
2022-04-08 05:44:28 +00:00
/>
</b-card>
2020-07-26 17:35:56 +00:00
</div>
2020-07-26 11:44:34 +00:00
</div>
</template>
<script lang="ts">
import TimeRangeSelect from '@/components/ftbot/TimeRangeSelect.vue';
2020-07-26 17:35:56 +00:00
import BacktestResultView from '@/components/ftbot/BacktestResultView.vue';
2021-01-20 06:55:56 +00:00
import BacktestResultSelect from '@/components/ftbot/BacktestResultSelect.vue';
2020-09-12 17:21:35 +00:00
import CandleChartContainer from '@/components/charts/CandleChartContainer.vue';
2021-05-24 09:13:11 +00:00
import StrategySelect from '@/components/ftbot/StrategySelect.vue';
2020-12-05 16:42:22 +00:00
import PairSummary from '@/components/ftbot/PairSummary.vue';
2021-05-23 14:54:38 +00:00
import TimeframeSelect from '@/components/ftbot/TimeframeSelect.vue';
2022-04-08 05:44:28 +00:00
import TradeList from '@/components/ftbot/TradeList.vue';
2022-07-31 16:03:46 +00:00
import TradeListNav from '@/components/ftbot/TradeListNav.vue';
import BacktestHistoryLoad from '@/components/ftbot/BacktestHistoryLoad.vue';
import BacktestGraphsView from '@/components/ftbot/BacktestGraphsView.vue';
2020-07-26 17:35:56 +00:00
2022-08-01 00:58:50 +00:00
import { BacktestPayload, ChartSliderPosition, Trade } from '@/types';
2020-07-27 05:19:45 +00:00
2021-01-14 07:04:28 +00:00
import { formatPercent } from '@/shared/formatters';
2022-07-07 18:44:19 +00:00
import { defineComponent, computed, ref, onMounted, watch } from 'vue';
2022-04-19 05:05:34 +00:00
import { useBotStore } from '@/stores/ftbotwrapper';
2020-07-26 11:44:34 +00:00
2022-04-16 12:30:33 +00:00
export default defineComponent({
name: 'Backtesting',
2020-11-01 12:47:57 +00:00
components: {
BacktestResultView,
BacktestGraphsView,
2021-01-20 06:55:56 +00:00
BacktestResultSelect,
BacktestHistoryLoad,
2020-11-01 12:47:57 +00:00
TimeRangeSelect,
CandleChartContainer,
2021-05-24 09:13:11 +00:00
StrategySelect,
2020-12-05 16:42:22 +00:00
PairSummary,
2021-05-23 14:54:38 +00:00
TimeframeSelect,
2022-04-08 05:44:28 +00:00
TradeList,
2022-08-01 22:50:36 +00:00
TradeListNav,
2020-11-01 12:47:57 +00:00
},
2022-04-16 12:30:33 +00:00
setup() {
2022-04-19 05:05:34 +00:00
const botStore = useBotStore();
2022-04-16 12:30:33 +00:00
const hasBacktestResult = computed(() =>
2022-04-19 05:05:34 +00:00
botStore.activeBot.backtestHistory
? Object.keys(botStore.activeBot.backtestHistory).length !== 0
: false,
2022-04-16 12:30:33 +00:00
);
const timeframe = computed((): string => {
try {
2022-04-19 05:05:34 +00:00
return botStore.activeBot.selectedBacktestResult.timeframe;
2022-04-16 12:30:33 +00:00
} catch (err) {
return '';
}
});
const strategy = ref('');
const selectedTimeframe = ref('');
const selectedDetailTimeframe = ref('');
const timerange = ref('');
const showLeftBar = ref(false);
2022-08-01 00:58:50 +00:00
const showRightBar = ref(true);
2022-04-16 12:30:33 +00:00
const enableProtections = ref(false);
const stakeAmountUnlimited = ref(false);
const maxOpenTrades = ref('');
const stakeAmount = ref('');
const startingCapital = ref('');
const btFormMode = ref('run');
const pollInterval = ref<number | null>(null);
2022-08-02 18:36:47 +00:00
const sliderPosition = ref<ChartSliderPosition>();
2022-04-16 12:30:33 +00:00
2022-09-14 18:44:29 +00:00
const selectBacktestResult = () => {
2022-04-16 12:30:33 +00:00
// Set parameters for this result
2022-04-19 05:05:34 +00:00
strategy.value = botStore.activeBot.selectedBacktestResult.strategy_name;
selectedTimeframe.value = botStore.activeBot.selectedBacktestResult.timeframe;
selectedDetailTimeframe.value =
botStore.activeBot.selectedBacktestResult.timeframe_detail || '';
timerange.value = botStore.activeBot.selectedBacktestResult.timerange;
2020-07-26 11:44:34 +00:00
};
2022-04-16 12:30:33 +00:00
2022-09-14 18:44:29 +00:00
watch(
() => botStore.activeBot.selectedBacktestResultKey,
() => {
selectBacktestResult();
},
);
2022-04-16 12:30:33 +00:00
const clickBacktest = () => {
const btPayload: BacktestPayload = {
strategy: strategy.value,
timerange: timerange.value,
// eslint-disable-next-line @typescript-eslint/camelcase
enable_protections: enableProtections.value,
};
const openTradesInt = parseInt(maxOpenTrades.value, 10);
if (openTradesInt) {
2021-02-28 08:53:51 +00:00
// eslint-disable-next-line @typescript-eslint/camelcase
2022-04-16 12:30:33 +00:00
btPayload.max_open_trades = openTradesInt;
}
if (stakeAmountUnlimited.value) {
// eslint-disable-next-line @typescript-eslint/camelcase
btPayload.stake_amount = 'unlimited';
} else {
const stakeAmountLoc = Number(stakeAmount.value);
if (stakeAmountLoc) {
// eslint-disable-next-line @typescript-eslint/camelcase
btPayload.stake_amount = stakeAmountLoc.toString();
}
2021-02-28 08:53:51 +00:00
}
2022-04-16 12:30:33 +00:00
const startingCapitalLoc = Number(startingCapital.value);
if (startingCapitalLoc) {
// eslint-disable-next-line @typescript-eslint/camelcase
btPayload.dry_run_wallet = startingCapitalLoc;
}
2020-12-08 18:35:06 +00:00
2022-04-16 12:30:33 +00:00
if (selectedTimeframe.value) {
btPayload.timeframe = selectedTimeframe.value;
}
if (selectedDetailTimeframe.value) {
// eslint-disable-next-line @typescript-eslint/camelcase
btPayload.timeframe_detail = selectedDetailTimeframe.value;
}
2020-07-26 11:44:34 +00:00
2022-04-19 05:05:34 +00:00
botStore.activeBot.startBacktest(btPayload);
2022-04-16 12:30:33 +00:00
};
2022-08-01 00:58:50 +00:00
2022-08-01 22:50:36 +00:00
const navigateChartToTrade = (trade: Trade) => {
2022-08-01 00:58:50 +00:00
sliderPosition.value = {
startValue: trade.open_timestamp,
endValue: trade.close_timestamp,
2022-08-01 22:50:36 +00:00
};
2022-08-01 00:58:50 +00:00
};
2022-04-19 05:05:34 +00:00
onMounted(() => botStore.activeBot.getState());
watch(
() => botStore.activeBot.backtestRunning,
() => {
if (botStore.activeBot.backtestRunning === true) {
pollInterval.value = window.setInterval(botStore.activeBot.pollBacktest, 1000);
} else if (pollInterval.value) {
clearInterval(pollInterval.value);
pollInterval.value = null;
}
},
);
2022-04-16 12:30:33 +00:00
return {
2022-04-19 05:05:34 +00:00
botStore,
2022-04-16 12:30:33 +00:00
formatPercent,
hasBacktestResult,
timeframe,
strategy,
selectedTimeframe,
selectedDetailTimeframe,
timerange,
enableProtections,
showLeftBar,
2022-08-01 00:58:50 +00:00
showRightBar,
2022-04-16 12:30:33 +00:00
stakeAmountUnlimited,
maxOpenTrades,
stakeAmount,
startingCapital,
btFormMode,
clickBacktest,
2022-08-01 00:58:50 +00:00
navigateChartToTrade,
2022-08-01 22:50:36 +00:00
sliderPosition,
2022-04-16 12:30:33 +00:00
};
},
});
2020-07-26 11:44:34 +00:00
</script>
2021-05-24 09:34:48 +00:00
<style lang="scss" scoped>
.candle-chart-container {
2021-07-04 17:57:19 +00:00
// TODO: Rough estimate - still to fix correctly
// Applies to all "calc" usages in this file.
height: calc(100vh - 250px) !important;
}
2021-01-06 13:51:04 +00:00
2021-01-21 06:47:51 +00:00
.bt-running-label {
position: absolute;
right: 2em;
margin-top: 1em;
}
2021-03-11 18:17:09 +00:00
.sticky-offset {
top: 2em;
}
2021-05-24 09:34:48 +00:00
.flex-samesize-items {
flex: 1 1 0;
@media md {
flex: unset;
}
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
2021-07-06 04:56:41 +00:00
.bt-config {
@media (min-width: 992px) {
margin-left: auto;
margin-right: auto;
max-width: 75vw;
}
}
</style>