frequi_origin/src/components/ftbot/BacktestResultAnalysis.vue

405 lines
13 KiB
Vue
Raw Normal View History

2020-07-26 17:35:56 +00:00
<template>
<div class="px-0 mw-100">
<div class="d-flex justify-content-center">
2021-03-10 15:09:55 +00:00
<h3>Backtest-result for {{ backtestResult.strategy_name }}</h3>
2020-07-26 17:35:56 +00:00
</div>
2021-03-11 18:17:09 +00:00
<div class="d-flex flex-column text-start ms-0 me-2 gap-2">
2023-08-16 09:57:12 +00:00
<div class="d-flex flex-column flex-xl-row">
<div class="px-0 px-xl-0 pe-xl-1 flex-fill">
2021-07-04 17:57:19 +00:00
<b-card header="Strategy settings">
<b-table
small
borderless
:items="backtestResultSettings"
:fields="backtestsettingFields"
>
</b-table>
</b-card>
</div>
2023-08-16 09:57:12 +00:00
<div class="px-0 px-xl-0 pt-2 pt-xl-0 ps-xl-1 flex-fill">
2021-07-04 17:57:19 +00:00
<b-card header="Metrics">
<b-table small borderless :items="backtestResultStats" :fields="backtestResultFields">
</b-table>
</b-card>
</div>
2021-01-19 21:50:51 +00:00
</div>
<b-card header="Results per Exit-reason">
2021-07-17 15:31:01 +00:00
<b-table
small
hover
stacked="sm"
2023-07-11 20:02:02 +00:00
:items="
(backtestResult.exit_reason_summary ||
backtestResult.sell_reason_summary) as unknown as TableItem[]
"
2022-04-06 18:38:38 +00:00
:fields="perExitReason"
2021-07-17 15:31:01 +00:00
>
2020-07-26 17:36:47 +00:00
</b-table>
</b-card>
<b-card header="Results per pair">
2021-07-17 15:31:01 +00:00
<b-table
small
hover
stacked="sm"
2023-04-06 05:16:57 +00:00
:items="backtestResult.results_per_pair as unknown as TableItem[]"
2021-07-17 15:31:01 +00:00
:fields="perPairFields"
>
2021-01-06 19:25:38 +00:00
</b-table>
</b-card>
<b-card v-if="backtestResult.periodic_breakdown" header="Periodic breakdown">
2023-07-11 20:02:02 +00:00
<BacktestResultPeriodBreakdown :periodic-breakdown="backtestResult.periodic_breakdown">
</BacktestResultPeriodBreakdown>
</b-card>
2020-07-26 17:36:47 +00:00
<b-card header="Single trades">
2021-07-04 17:57:19 +00:00
<TradeList
:trades="backtestResult.trades"
2021-08-09 17:56:11 +00:00
:show-filter="true"
:stake-currency="backtestResult.stake_currency"
2021-07-04 17:57:19 +00:00
/>
</b-card>
2020-07-26 17:35:56 +00:00
</div>
</div>
</template>
2022-12-10 13:17:13 +00:00
<script setup lang="ts">
2020-07-26 17:35:56 +00:00
import TradeList from '@/components/ftbot/TradeList.vue';
2021-01-21 06:47:51 +00:00
import { StrategyBacktestResult, Trade } from '@/types';
import BacktestResultPeriodBreakdown from './BacktestResultPeriodBreakdown.vue';
import { formatObjectForTable } from '@/shared/objectToTableItems';
2020-07-26 17:35:56 +00:00
import { computed } from 'vue';
2020-09-12 17:21:35 +00:00
import {
timestampms,
formatPercent,
formatPrice,
humanizeDurationFromSeconds,
isNotUndefined,
2020-09-12 17:21:35 +00:00
} from '@/shared/formatters';
2023-04-06 05:16:57 +00:00
import { TableField, TableItem } from 'bootstrap-vue-next';
2020-07-26 17:35:56 +00:00
2022-12-10 13:17:13 +00:00
const props = defineProps({
backtestResult: { required: true, type: Object as () => StrategyBacktestResult },
});
2021-01-21 06:47:51 +00:00
2022-12-10 13:17:13 +00:00
const formatPriceStake = (price) => {
return `${formatPrice(price, props.backtestResult.stake_currency_decimals)} ${
props.backtestResult.stake_currency
}`;
};
const getSortedTrades = (backtestResult: StrategyBacktestResult): Trade[] => {
const sortedTrades = backtestResult.trades
.slice()
.sort((a, b) => a.profit_ratio - b.profit_ratio);
return sortedTrades;
};
2020-09-12 17:21:35 +00:00
2022-12-10 13:17:13 +00:00
const bestPair = computed((): string => {
const trades = getSortedTrades(props.backtestResult);
if (trades.length === 0) {
return 'N/A';
}
2022-12-10 13:17:13 +00:00
const value = trades[trades.length - 1];
return `${value.pair} ${formatPercent(value.profit_ratio, 2)}`;
});
const worstPair = computed((): string => {
const trades = getSortedTrades(props.backtestResult);
if (trades.length === 0) {
return 'N/A';
}
2022-12-10 13:17:13 +00:00
const value = trades[0];
return `${value.pair} ${formatPercent(value.profit_ratio, 2)}`;
});
const pairSummary = computed(() => {
return props.backtestResult.results_per_pair[props.backtestResult.results_per_pair.length - 1];
});
2022-12-10 13:17:13 +00:00
const backtestResultStats = computed(() => {
// Transpose Result into readable format
const shortMetrics =
props.backtestResult?.trade_count_short && props.backtestResult?.trade_count_short > 0
? [
{ '___ ': '___' },
2022-12-10 13:17:13 +00:00
{
'Long / Short': `${props.backtestResult.trade_count_long} / ${props.backtestResult.trade_count_short}`,
2022-12-10 13:17:13 +00:00
},
{
'Total profit Long': `${formatPercent(
2022-12-10 13:17:13 +00:00
props.backtestResult.profit_total_long || 0,
)} | ${formatPriceStake(props.backtestResult.profit_total_long_abs)}`,
},
{
'Total profit Short': `${formatPercent(
2022-12-10 13:17:13 +00:00
props.backtestResult.profit_total_short || 0,
)} | ${formatPriceStake(props.backtestResult.profit_total_short_abs)}`,
},
]
: [];
2021-05-23 14:25:31 +00:00
const tmp = [
2022-12-10 13:17:13 +00:00
{
'Total Profit': `${formatPercent(props.backtestResult.profit_total)} | ${formatPriceStake(
2022-12-10 13:17:13 +00:00
props.backtestResult.profit_total_abs,
)}`,
},
{
CAGR: `${props.backtestResult.cagr ? formatPercent(props.backtestResult.cagr) : 'N/A'}`,
2022-12-10 13:17:13 +00:00
},
2022-12-29 16:49:41 +00:00
{
Sortino: `${props.backtestResult.sortino ? props.backtestResult.sortino.toFixed(2) : 'N/A'}`,
2022-12-29 16:49:41 +00:00
},
{
Sharpe: `${props.backtestResult.sharpe ? props.backtestResult.sharpe.toFixed(2) : 'N/A'}`,
2022-12-29 16:49:41 +00:00
},
{
Calmar: `${props.backtestResult.calmar ? props.backtestResult.calmar.toFixed(2) : 'N/A'}`,
2022-12-29 16:49:41 +00:00
},
{
[`Expectancy ${props.backtestResult.expectancy_ratio ? '(ratio)' : ''}`]: `${
props.backtestResult.expectancy
? props.backtestResult.expectancy_ratio
? props.backtestResult.expectancy.toFixed(2) +
' (' +
props.backtestResult.expectancy_ratio.toFixed(2) +
')'
: props.backtestResult.expectancy.toFixed(2)
: 'N/A'
2022-12-29 16:49:41 +00:00
}`,
},
2022-12-10 13:17:13 +00:00
{
'Profit factor': `${
2022-12-10 13:17:13 +00:00
props.backtestResult.profit_factor
? formatPrice(props.backtestResult.profit_factor, 3)
: 'N/A'
}`,
},
{
'Total trades / Daily Avg Trades': `${props.backtestResult.total_trades} / ${props.backtestResult.trades_per_day}`,
2022-12-10 13:17:13 +00:00
},
// { 'First trade': props.backtestResult.backtest_fi },
// { 'First trade Pair': props.backtestResult.backtest_best_day },
2022-12-10 13:17:13 +00:00
{
'Best day': `${formatPercent(props.backtestResult.backtest_best_day, 2)} | ${formatPriceStake(
2022-12-10 13:17:13 +00:00
props.backtestResult.backtest_best_day_abs,
)}`,
},
{
'Worst day': `${formatPercent(
props.backtestResult.backtest_worst_day,
2,
)} | ${formatPriceStake(props.backtestResult.backtest_worst_day_abs)}`,
2022-12-10 13:17:13 +00:00
},
2022-04-07 04:59:08 +00:00
2022-12-10 13:17:13 +00:00
{
'Win/Draw/Loss': `${pairSummary.value.wins} / ${pairSummary.value.draws} / ${
pairSummary.value.losses
} ${
isNotUndefined(pairSummary.value.winrate)
? '(WR: ' +
formatPercent(
props.backtestResult.results_per_pair[
props.backtestResult.results_per_pair.length - 1
].winrate ?? 0,
2,
) +
')'
: ''
2022-12-10 13:17:13 +00:00
}`,
},
{
'Days win/draw/loss': `${props.backtestResult.winning_days} / ${props.backtestResult.draw_days} / ${props.backtestResult.losing_days}`,
2022-12-10 13:17:13 +00:00
},
{
'Avg. Duration winners': humanizeDurationFromSeconds(
props.backtestResult.winner_holding_avg_s,
),
2022-12-10 13:17:13 +00:00
},
{
'Avg. Duration Losers': humanizeDurationFromSeconds(props.backtestResult.loser_holding_avg_s),
2022-12-10 13:17:13 +00:00
},
2023-07-25 18:05:00 +00:00
{
'Max Consecutive Wins / Loss':
2023-07-25 18:05:00 +00:00
props.backtestResult.max_consecutive_wins === undefined
? 'N/A'
: `${props.backtestResult.max_consecutive_wins} / ${props.backtestResult.max_consecutive_losses}`,
},
{ 'Rejected entry signals': props.backtestResult.rejected_signals },
2022-12-10 13:17:13 +00:00
{
'Entry/Exit timeouts': `${props.backtestResult.timedout_entry_orders} / ${props.backtestResult.timedout_exit_orders}`,
2022-12-10 13:17:13 +00:00
},
{
'Canceled Trade Entries': props.backtestResult.canceled_trade_entries ?? 'N/A',
2022-12-10 13:17:13 +00:00
},
{
'Canceled Entry Orders': props.backtestResult.canceled_entry_orders ?? 'N/A',
2022-12-10 13:17:13 +00:00
},
{
'Replaced Entry Orders': props.backtestResult.replaced_entry_orders ?? 'N/A',
2022-12-10 13:17:13 +00:00
},
2020-07-26 17:35:56 +00:00
2022-12-10 13:17:13 +00:00
...shortMetrics,
2021-03-11 18:17:09 +00:00
{ ___: '___' },
{ 'Min balance': formatPriceStake(props.backtestResult.csum_min) },
{ 'Max balance': formatPriceStake(props.backtestResult.csum_max) },
{ 'Market change': formatPercent(props.backtestResult.market_change) },
{ '___ ': '___' },
2022-12-10 13:17:13 +00:00
{
'Max Drawdown (Account)': formatPercent(props.backtestResult.max_drawdown_account),
2022-12-10 13:17:13 +00:00
},
{
'Max Drawdown ABS': formatPriceStake(props.backtestResult.max_drawdown_abs),
2022-12-10 13:17:13 +00:00
},
{
'Drawdown high | low': `${formatPriceStake(
props.backtestResult.max_drawdown_high,
)} | ${formatPriceStake(props.backtestResult.max_drawdown_low)}`,
2022-12-10 13:17:13 +00:00
},
{ 'Drawdown start': timestampms(props.backtestResult.drawdown_start_ts) },
{ 'Drawdown end': timestampms(props.backtestResult.drawdown_end_ts) },
{ '___ ': '___' },
2021-03-10 15:34:09 +00:00
2022-12-10 13:17:13 +00:00
{
'Best Pair': `${props.backtestResult.best_pair.key} ${formatPercent(
2022-12-10 13:17:13 +00:00
props.backtestResult.best_pair.profit_sum,
)}`,
},
{
'Worst Pair': `${props.backtestResult.worst_pair.key} ${formatPercent(
2022-12-10 13:17:13 +00:00
props.backtestResult.worst_pair.profit_sum,
)}`,
},
{ 'Best single Trade': bestPair.value },
{ 'Worst single Trade': worstPair.value },
2022-12-10 13:17:13 +00:00
];
return formatObjectForTable({ value: tmp }, 'metric');
2022-12-10 13:17:13 +00:00
});
2021-01-19 21:50:51 +00:00
2022-12-10 13:17:13 +00:00
const backtestResultSettings = computed(() => {
// Transpose Result into readable format
const tmp = [
{ 'Backtesting from': timestampms(props.backtestResult.backtest_start_ts) },
{ 'Backtesting to': timestampms(props.backtestResult.backtest_end_ts) },
2022-12-10 13:17:13 +00:00
{
'BT execution time': humanizeDurationFromSeconds(
2022-12-10 13:17:13 +00:00
props.backtestResult.backtest_run_end_ts - props.backtestResult.backtest_run_start_ts,
),
},
{ 'Max open trades': props.backtestResult.max_open_trades },
{ Timeframe: props.backtestResult.timeframe },
{ 'Timeframe Detail': props.backtestResult.timeframe_detail || 'N/A' },
{ Timerange: props.backtestResult.timerange },
{ Stoploss: formatPercent(props.backtestResult.stoploss, 2) },
{ 'Trailing Stoploss': props.backtestResult.trailing_stop },
2022-12-10 13:17:13 +00:00
{
'Trail only when offset is reached': props.backtestResult.trailing_only_offset_is_reached,
2022-12-10 13:17:13 +00:00
},
{ 'Trailing Stop positive': props.backtestResult.trailing_stop_positive },
2022-12-10 13:17:13 +00:00
{
'Trailing stop positive offset': props.backtestResult.trailing_stop_positive_offset,
2022-12-10 13:17:13 +00:00
},
{ 'Custom Stoploss': props.backtestResult.use_custom_stoploss },
{ ROI: props.backtestResult.minimal_roi },
2022-12-10 13:17:13 +00:00
{
'Use Exit Signal':
2022-12-10 13:17:13 +00:00
props.backtestResult.use_exit_signal !== undefined
? props.backtestResult.use_exit_signal
: props.backtestResult.use_sell_signal,
},
{
'Exit profit only':
2022-12-10 13:17:13 +00:00
props.backtestResult.exit_profit_only !== undefined
? props.backtestResult.exit_profit_only
: props.backtestResult.sell_profit_only,
},
{
'Exit profit offset':
2022-12-10 13:17:13 +00:00
props.backtestResult.exit_profit_offset !== undefined
? props.backtestResult.exit_profit_offset
: props.backtestResult.sell_profit_offset,
},
{ 'Enable protections': props.backtestResult.enable_protections },
2022-12-10 13:17:13 +00:00
{
'Starting balance': formatPriceStake(props.backtestResult.starting_balance),
2022-12-10 13:17:13 +00:00
},
{
'Final balance': formatPriceStake(props.backtestResult.final_balance),
2022-12-10 13:17:13 +00:00
},
{
'Avg. stake amount': formatPriceStake(props.backtestResult.avg_stake_amount),
2022-12-10 13:17:13 +00:00
},
{
'Total trade volume': formatPriceStake(props.backtestResult.total_volume),
2022-12-10 13:17:13 +00:00
},
];
return formatObjectForTable({ value: tmp }, 'setting');
2022-12-10 13:17:13 +00:00
});
const perPairFields = computed(() => {
return [
{ key: 'key', label: 'Pair' },
{ key: 'trades', label: 'Buys' },
{
key: 'profit_mean',
label: 'Avg Profit %',
formatter: (value) => formatPercent(value, 2),
},
{ key: 'profit_sum', label: 'Cum Profit %', formatter: (value) => formatPercent(value, 2) },
{
key: 'profit_total_abs',
label: `Tot Profit ${props.backtestResult.stake_currency}`,
formatter: (value) => formatPrice(value, props.backtestResult.stake_currency_decimals),
},
{
key: 'profit_total',
label: 'Tot Profit %',
formatter: (value) => formatPercent(value, 2),
},
{ key: 'duration_avg', label: 'Avg Duration' },
{ key: 'wins', label: 'Wins' },
{ key: 'draws', label: 'Draws' },
{ key: 'losses', label: 'Losses' },
];
});
2020-07-26 17:36:47 +00:00
2022-12-10 13:17:13 +00:00
const perExitReason = computed(() => {
return [
{ key: 'exit_reason', label: 'Exit Reason' },
{ key: 'trades', label: 'Buys' },
{
key: 'profit_mean',
label: 'Avg Profit %',
formatter: (value) => formatPercent(value, 2),
},
{ key: 'profit_sum', label: 'Cum Profit %', formatter: (value) => formatPercent(value, 2) },
{
key: 'profit_total_abs',
label: `Tot Profit ${props.backtestResult.stake_currency}`,
2021-01-19 21:50:51 +00:00
2022-12-10 13:17:13 +00:00
formatter: (value) => formatPrice(value, props.backtestResult.stake_currency_decimals),
},
{
key: 'profit_total',
label: 'Tot Profit %',
formatter: (value) => formatPercent(value, 2),
},
{ key: 'wins', label: 'Wins' },
{ key: 'draws', label: 'Draws' },
{ key: 'losses', label: 'Losses' },
];
});
const backtestResultFields: TableField[] = [
2022-12-10 13:17:13 +00:00
{ key: 'metric', label: 'Metric' },
{ key: 'value', label: 'Value' },
];
const backtestsettingFields: TableField[] = [
2022-12-10 13:17:13 +00:00
{ key: 'setting', label: 'Setting' },
{ key: 'value', label: 'Value' },
];
2020-07-26 17:35:56 +00:00
</script>
<style lang="scss" scoped></style>