frequi_origin/src/components/charts/CandleChartContainer.vue

289 lines
8.1 KiB
Vue
Raw Normal View History

2020-08-08 13:37:18 +00:00
<template>
2021-06-22 19:04:33 +00:00
<div class="d-flex h-100">
<div class="flex-fill container-fluid flex-column align-items-stretch d-flex h-100">
<b-modal
v-if="plotConfigModal"
2021-06-22 19:04:33 +00:00
id="plotConfiguratorModal"
title="Plot Configurator"
ok-only
hide-backdrop
button-size="sm"
>
<PlotConfigurator v-model="plotConfig" :columns="datasetColumns" />
</b-modal>
<div class="row mr-0">
2021-11-02 06:11:02 +00:00
<div class="ml-2 d-flex flex-wrap flex-md-nowrap align-items-center">
<span class="ml-2 text-nowrap">{{ strategyName }} | {{ timeframe || '' }}</span>
<v-select
v-model="pair"
class="ml-2"
:options="availablePairs"
2021-11-02 06:11:02 +00:00
style="min-width: 7em"
size="sm"
:clearable="false"
@change="refresh"
>
</v-select>
<b-button class="ml-2" :disabled="!!!pair" size="sm" @click="refresh">&#x21bb;</b-button>
2022-04-02 17:23:32 +00:00
<small v-if="dataset" class="ml-2 text-nowrap" title="Long entry signals"
>Long signals: {{ dataset.enter_long_signals || dataset.buy_signals }}</small
>
2022-04-02 17:23:32 +00:00
<small v-if="dataset" class="ml-2 text-nowrap" title="Long exit signals"
>Long exit: {{ dataset.exit_long_signals || dataset.sell_signals }}</small
>
2021-11-20 19:11:54 +00:00
<small v-if="dataset && dataset.enter_short_signals" class="ml-2 text-nowrap"
>Short entries: {{ dataset.enter_short_signals }}</small
>
<small v-if="dataset && dataset.exit_short_signals" class="ml-2 text-nowrap"
>Short exits: {{ dataset.exit_short_signals }}</small
>
2021-06-22 19:04:33 +00:00
</div>
2022-03-11 06:48:39 +00:00
<div class="ml-auto d-flex align-items-center">
<b-form-checkbox v-model="heikinAshi">Heikin Ashi</b-form-checkbox>
<div class="ml-2">
<b-select
v-model="plotConfigName"
:options="availablePlotConfigNames"
size="sm"
@change="plotConfigChanged"
>
</b-select>
</div>
2021-06-22 19:04:33 +00:00
2022-03-11 06:48:39 +00:00
<div class="ml-2 mr-0 mr-md-1">
<b-button size="sm" title="Plot configurator" @click="showConfigurator">
&#9881;
</b-button>
</div>
2021-06-22 19:04:33 +00:00
</div>
2020-09-27 07:24:12 +00:00
</div>
2021-06-22 19:04:33 +00:00
<div class="row mr-1 ml-1 h-100">
<CandleChart
v-if="hasDataset"
:dataset="dataset"
:trades="trades"
:plot-config="plotConfig"
2022-03-11 06:48:39 +00:00
:heikin-ashi="heikinAshi"
2021-07-01 05:15:11 +00:00
:use-u-t-c="timezone === 'UTC'"
2021-06-22 19:04:33 +00:00
:theme="getChartTheme"
2020-08-08 13:57:36 +00:00
>
2021-06-22 19:04:33 +00:00
</CandleChart>
2022-03-03 18:37:38 +00:00
<div v-else class="m-auto">
2022-03-03 18:46:42 +00:00
<b-spinner v-if="isLoadingDataset" label="Spinning" />
2022-03-03 18:37:38 +00:00
2022-03-03 18:46:42 +00:00
<div v-else style="font-size: 1.5rem">
2022-03-03 18:37:38 +00:00
{{ noDatasetText }}
</div>
</div>
2020-08-08 13:37:18 +00:00
</div>
</div>
2021-06-22 19:04:33 +00:00
<transition name="fade" mode="in-out">
<div v-if="!plotConfigModal" v-show="showPlotConfig" class="w-25 config-sidebar">
2021-06-23 04:45:18 +00:00
<PlotConfigurator v-model="plotConfig" :columns="datasetColumns" :as-modal="false" />
2021-06-22 19:04:33 +00:00
</div>
</transition>
2020-08-08 13:37:18 +00:00
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
2020-12-28 19:52:18 +00:00
import { Getter, namespace } from 'vuex-class';
import {
Trade,
PairHistory,
EMPTY_PLOTCONFIG,
PlotConfig,
PairCandlePayload,
PairHistoryPayload,
2022-03-03 19:16:21 +00:00
LoadingStatus,
} from '@/types';
import CandleChart from '@/components/charts/CandleChart.vue';
import PlotConfigurator from '@/components/charts/PlotConfigurator.vue';
2020-08-08 13:57:36 +00:00
import { getCustomPlotConfig, getPlotConfigName } from '@/shared/storage';
2020-10-31 07:47:07 +00:00
import { BotStoreGetters } from '@/store/modules/ftbot';
import vSelect from 'vue-select';
import StoreModules from '@/store/storeSubModules';
2022-04-13 19:28:04 +00:00
import { useSettingsStore } from '@/stores/settings';
2020-08-08 13:57:36 +00:00
const ftbot = namespace(StoreModules.ftbot);
2020-08-08 13:37:18 +00:00
@Component({ components: { CandleChart, PlotConfigurator, vSelect } })
2020-08-08 13:37:18 +00:00
export default class CandleChartContainer extends Vue {
@Prop({ required: true }) readonly availablePairs!: string[];
2020-08-08 13:37:18 +00:00
@Prop({ required: true }) readonly timeframe!: string;
@Prop({ required: false, default: () => [] }) readonly trades!: Array<Trade>;
2020-08-08 13:37:18 +00:00
@Prop({ required: false, default: false }) historicView!: boolean;
2021-06-22 19:04:33 +00:00
@Prop({ required: false, default: true }) plotConfigModal!: boolean;
/** Only required if historicView is true */
@Prop({ required: false, default: false }) timerange!: string;
/**
* Only required if historicView is true
*/
@Prop({ required: false, default: false }) strategy!: string;
pair = '';
2020-08-08 13:37:18 +00:00
plotConfig: PlotConfig = { ...EMPTY_PLOTCONFIG };
2020-08-08 13:57:36 +00:00
plotConfigName = '';
2021-06-25 16:46:30 +00:00
showPlotConfig = this.plotConfigModal;
2021-06-22 19:04:33 +00:00
2022-03-11 06:48:39 +00:00
heikinAshi: boolean = false;
2020-12-28 19:52:18 +00:00
@Getter getChartTheme!: string;
@ftbot.Getter [BotStoreGetters.availablePlotConfigNames]!: string[];
2020-08-08 13:57:36 +00:00
2020-08-24 18:20:54 +00:00
@ftbot.Action setPlotConfigName;
2022-03-03 19:16:21 +00:00
@ftbot.Getter [BotStoreGetters.candleDataStatus]!: LoadingStatus;
2022-03-03 18:37:38 +00:00
@ftbot.Getter [BotStoreGetters.candleData]!: PairHistory;
2022-03-03 19:16:21 +00:00
@ftbot.Getter [BotStoreGetters.historyStatus]!: LoadingStatus;
2022-03-03 18:37:38 +00:00
@ftbot.Getter [BotStoreGetters.history]!: PairHistory;
2020-10-31 07:47:07 +00:00
@ftbot.Getter [BotStoreGetters.selectedPair]!: string;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ftbot.Action public getPairCandles!: (payload: PairCandlePayload) => void;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ftbot.Action public getPairHistory!: (payload: PairHistoryPayload) => void;
2020-09-27 07:24:12 +00:00
get dataset(): PairHistory {
if (this.historicView) {
return this.history[`${this.pair}__${this.timeframe}`];
}
return this.candleData[`${this.pair}__${this.timeframe}`];
}
get strategyName() {
return this.strategy || this.dataset?.strategy || '';
}
2020-08-08 13:37:18 +00:00
get datasetColumns() {
return this.dataset ? this.dataset.columns : [];
}
2022-03-03 18:37:38 +00:00
get isLoadingDataset(): boolean {
if (this.historicView) {
return this.historyStatus === 'loading';
}
return this.candleDataStatus === 'loading';
}
get noDatasetText(): string {
const status = this.historicView ? this.historyStatus : this.candleDataStatus;
switch (status) {
case 'loading':
return 'Loading...';
case 'success':
return 'No data available';
case 'error':
return 'Failed to load data';
default:
return 'Unknown';
}
}
2020-09-27 07:37:07 +00:00
get hasDataset(): boolean {
return !!this.dataset;
}
2022-04-13 19:28:04 +00:00
timezone: string = 'UTC';
2020-08-08 13:37:18 +00:00
mounted() {
2022-04-13 19:28:04 +00:00
const settingsStore = useSettingsStore();
this.timezone = settingsStore.timezone;
settingsStore.$subscribe((_, state) => {
this.timezone = state.timezone;
});
if (this.selectedPair) {
this.pair = this.selectedPair;
} else if (this.availablePairs.length > 0) {
[this.pair] = this.availablePairs;
}
2020-08-08 13:57:36 +00:00
this.plotConfigName = getPlotConfigName();
this.plotConfig = getCustomPlotConfig(this.plotConfigName);
2022-03-03 19:06:44 +00:00
if (!this.hasDataset) {
this.refresh();
}
2020-08-08 13:57:36 +00:00
}
plotConfigChanged() {
2020-08-24 18:20:54 +00:00
console.log('plotConfigChanged');
2020-08-08 13:57:36 +00:00
this.plotConfig = getCustomPlotConfig(this.plotConfigName);
2020-08-24 18:20:54 +00:00
this.setPlotConfigName(this.plotConfigName);
2020-08-08 13:37:18 +00:00
}
showConfigurator() {
2021-06-22 19:04:33 +00:00
if (this.plotConfigModal) {
this.$bvModal.show('plotConfiguratorModal');
} else {
this.showPlotConfig = !this.showPlotConfig;
}
2020-08-08 13:37:18 +00:00
}
refresh() {
if (this.pair && this.timeframe) {
if (this.historicView) {
this.getPairHistory({
pair: this.pair,
timeframe: this.timeframe,
timerange: this.timerange,
strategy: this.strategy,
});
} else {
this.getPairCandles({ pair: this.pair, timeframe: this.timeframe, limit: 500 });
}
}
}
@Watch('availablePairs')
watchAvailablePairs() {
if (!this.availablePairs.find((pair) => pair === this.pair)) {
[this.pair] = this.availablePairs;
this.refresh();
}
}
2020-10-31 07:47:07 +00:00
@Watch(BotStoreGetters.selectedPair)
watchSelectedPair() {
this.pair = this.selectedPair;
this.refresh();
}
2020-08-08 13:37:18 +00:00
}
</script>
2021-06-22 19:04:33 +00:00
<style scoped lang="scss">
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>