mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-12 19:23:51 +00:00
chore: update block-order in components
script before template.
This commit is contained in:
parent
8b40c2a862
commit
dc3553bb3e
|
@ -1,21 +1,3 @@
|
||||||
<template>
|
|
||||||
<div class="d-flex flex-row">
|
|
||||||
<BFormGroup class="flex-grow-1" :label="label" label-for="indicatorSelector">
|
|
||||||
<VSelect
|
|
||||||
v-model="selAvailableIndicator"
|
|
||||||
:options="columns"
|
|
||||||
size="sm"
|
|
||||||
:clearable="false"
|
|
||||||
@option:selected="emitIndicator"
|
|
||||||
>
|
|
||||||
</VSelect>
|
|
||||||
</BFormGroup>
|
|
||||||
<BButton size="sm" title="Abort" class="ms-1 mt-auto" variant="secondary" @click="abort">
|
|
||||||
<i-mdi-close />
|
|
||||||
</BButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VSelect from 'vue-select';
|
import VSelect from 'vue-select';
|
||||||
|
|
||||||
|
@ -52,4 +34,22 @@ watch(
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex flex-row">
|
||||||
|
<BFormGroup class="flex-grow-1" :label="label" label-for="indicatorSelector">
|
||||||
|
<VSelect
|
||||||
|
v-model="selAvailableIndicator"
|
||||||
|
:options="columns"
|
||||||
|
size="sm"
|
||||||
|
:clearable="false"
|
||||||
|
@option:selected="emitIndicator"
|
||||||
|
>
|
||||||
|
</VSelect>
|
||||||
|
</BFormGroup>
|
||||||
|
<BButton size="sm" title="Abort" class="ms-1 mt-auto" variant="secondary" @click="abort">
|
||||||
|
<i-mdi-close />
|
||||||
|
</BButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,29 +1,3 @@
|
||||||
<template>
|
|
||||||
<div class="d-flex flex-column h-100 position-relative">
|
|
||||||
<div class="flex-grow-1">
|
|
||||||
<ECharts v-if="trades" :option="chartOptions" autoresize :theme="settingsStore.chartTheme" />
|
|
||||||
</div>
|
|
||||||
<BFormGroup
|
|
||||||
class="z-2"
|
|
||||||
:class="showTitle ? 'ms-5 ps-5' : 'position-absolute'"
|
|
||||||
label="Bins"
|
|
||||||
style="width: 33%; min-width: 12rem"
|
|
||||||
label-for="input-bins"
|
|
||||||
label-cols="6"
|
|
||||||
content-cols="6"
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
<BFormSelect
|
|
||||||
id="input-bins"
|
|
||||||
v-model="settingsStore.profitDistributionBins"
|
|
||||||
size="sm"
|
|
||||||
class="mt-1"
|
|
||||||
:options="binOptions"
|
|
||||||
></BFormSelect>
|
|
||||||
</BFormGroup>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ECharts from 'vue-echarts';
|
import ECharts from 'vue-echarts';
|
||||||
import { EChartsOption } from 'echarts';
|
import { EChartsOption } from 'echarts';
|
||||||
|
@ -143,6 +117,32 @@ const chartOptions = computed((): EChartsOption => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex flex-column h-100 position-relative">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<ECharts v-if="trades" :option="chartOptions" autoresize :theme="settingsStore.chartTheme" />
|
||||||
|
</div>
|
||||||
|
<BFormGroup
|
||||||
|
class="z-2"
|
||||||
|
:class="showTitle ? 'ms-5 ps-5' : 'position-absolute'"
|
||||||
|
label="Bins"
|
||||||
|
style="width: 33%; min-width: 12rem"
|
||||||
|
label-for="input-bins"
|
||||||
|
label-cols="6"
|
||||||
|
content-cols="6"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<BFormSelect
|
||||||
|
id="input-bins"
|
||||||
|
v-model="settingsStore.profitDistributionBins"
|
||||||
|
size="sm"
|
||||||
|
class="mt-1"
|
||||||
|
:options="binOptions"
|
||||||
|
></BFormSelect>
|
||||||
|
</BFormGroup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.echarts {
|
.echarts {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,14 +1,3 @@
|
||||||
<template>
|
|
||||||
<ECharts
|
|
||||||
v-if="dailyStats.data"
|
|
||||||
ref="dailyChart"
|
|
||||||
:option="dailyChartOptions"
|
|
||||||
:theme="settingsStore.chartTheme"
|
|
||||||
:style="{ height: width * 0.6 + 'px' }"
|
|
||||||
autoresize
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ECharts from 'vue-echarts';
|
import ECharts from 'vue-echarts';
|
||||||
// import { EChartsOption } from 'echarts';
|
// import { EChartsOption } from 'echarts';
|
||||||
|
@ -162,6 +151,17 @@ const dailyChartOptions: ComputedRef<EChartsOption> = computed(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ECharts
|
||||||
|
v-if="dailyStats.data"
|
||||||
|
ref="dailyChart"
|
||||||
|
:option="dailyChartOptions"
|
||||||
|
:theme="settingsStore.chartTheme"
|
||||||
|
:style="{ height: width * 0.6 + 'px' }"
|
||||||
|
autoresize
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.echarts {
|
.echarts {
|
||||||
min-height: 240px;
|
min-height: 240px;
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
<template>
|
|
||||||
<ECharts
|
|
||||||
v-if="trades.length > 0"
|
|
||||||
:option="chartOptions"
|
|
||||||
autoresize
|
|
||||||
:theme="settingsStore.chartTheme"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ECharts from 'vue-echarts';
|
import ECharts from 'vue-echarts';
|
||||||
import { EChartsOption } from 'echarts';
|
import { EChartsOption } from 'echarts';
|
||||||
|
@ -178,6 +169,15 @@ const chartOptions = computed((): EChartsOption => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ECharts
|
||||||
|
v-if="trades.length > 0"
|
||||||
|
:option="chartOptions"
|
||||||
|
autoresize
|
||||||
|
:theme="settingsStore.chartTheme"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.echarts {
|
.echarts {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,3 +1,30 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MessageBox, { MsgBoxObject } from '@/components/general/MessageBox.vue';
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
import { BacktestHistoryEntry } from '@/types';
|
||||||
|
import InfoBox from '../general/InfoBox.vue';
|
||||||
|
|
||||||
|
const botStore = useBotStore();
|
||||||
|
const msgBox = ref<typeof MessageBox>();
|
||||||
|
const filterText = ref('');
|
||||||
|
const filterTextDebounced = refDebounced(filterText, 350, { maxWait: 1000 });
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
botStore.activeBot.getBacktestHistory();
|
||||||
|
});
|
||||||
|
|
||||||
|
function deleteBacktestResult(result: BacktestHistoryEntry) {
|
||||||
|
const msg: MsgBoxObject = {
|
||||||
|
title: 'Delete result',
|
||||||
|
message: `Delete result ${result.filename} from disk?`,
|
||||||
|
accept: () => {
|
||||||
|
botStore.activeBot.deleteBacktestHistoryResult(result);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
msgBox.value?.show(msg);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
@ -104,33 +131,6 @@
|
||||||
<MessageBox ref="msgBox" />
|
<MessageBox ref="msgBox" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import MessageBox, { MsgBoxObject } from '@/components/general/MessageBox.vue';
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
import { BacktestHistoryEntry } from '@/types';
|
|
||||||
import InfoBox from '../general/InfoBox.vue';
|
|
||||||
|
|
||||||
const botStore = useBotStore();
|
|
||||||
const msgBox = ref<typeof MessageBox>();
|
|
||||||
const filterText = ref('');
|
|
||||||
const filterTextDebounced = refDebounced(filterText, 350, { maxWait: 1000 });
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
botStore.activeBot.getBacktestHistory();
|
|
||||||
});
|
|
||||||
|
|
||||||
function deleteBacktestResult(result: BacktestHistoryEntry) {
|
|
||||||
const msg: MsgBoxObject = {
|
|
||||||
title: 'Delete result',
|
|
||||||
message: `Delete result ${result.filename} from disk?`,
|
|
||||||
accept: () => {
|
|
||||||
botStore.activeBot.deleteBacktestHistoryResult(result);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
msgBox.value?.show(msg);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.table-rounded-corners {
|
.table-rounded-corners {
|
||||||
box-shadow: 0 0 0 1px var(--bs-border-color);
|
box-shadow: 0 0 0 1px var(--bs-border-color);
|
||||||
|
|
|
@ -1,3 +1,34 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { StrategyBacktestResult } from '@/types';
|
||||||
|
|
||||||
|
import { TableField } from 'bootstrap-vue-next';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
backtestResult: { required: true, type: Object as () => StrategyBacktestResult },
|
||||||
|
});
|
||||||
|
|
||||||
|
const backtestResultStats = computed(() => {
|
||||||
|
const tmp = generateBacktestMetricRows(props.backtestResult);
|
||||||
|
return formatObjectForTable({ value: tmp }, 'metric');
|
||||||
|
});
|
||||||
|
|
||||||
|
const backtestResultSettings = computed(() => {
|
||||||
|
// Transpose Result into readable format
|
||||||
|
const tmp = generateBacktestSettingRows(props.backtestResult);
|
||||||
|
|
||||||
|
return formatObjectForTable({ value: tmp }, 'setting');
|
||||||
|
});
|
||||||
|
const backtestResultFields: TableField[] = [
|
||||||
|
{ key: 'metric', label: 'Metric' },
|
||||||
|
{ key: 'value', label: 'Value' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const backtestsettingFields: TableField[] = [
|
||||||
|
{ key: 'setting', label: 'Setting' },
|
||||||
|
{ key: 'value', label: 'Value' },
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="px-0 mw-100">
|
<div class="px-0 mw-100">
|
||||||
<div class="d-flex justify-content-center">
|
<div class="d-flex justify-content-center">
|
||||||
|
@ -72,35 +103,4 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { StrategyBacktestResult } from '@/types';
|
|
||||||
|
|
||||||
import { TableField } from 'bootstrap-vue-next';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
backtestResult: { required: true, type: Object as () => StrategyBacktestResult },
|
|
||||||
});
|
|
||||||
|
|
||||||
const backtestResultStats = computed(() => {
|
|
||||||
const tmp = generateBacktestMetricRows(props.backtestResult);
|
|
||||||
return formatObjectForTable({ value: tmp }, 'metric');
|
|
||||||
});
|
|
||||||
|
|
||||||
const backtestResultSettings = computed(() => {
|
|
||||||
// Transpose Result into readable format
|
|
||||||
const tmp = generateBacktestSettingRows(props.backtestResult);
|
|
||||||
|
|
||||||
return formatObjectForTable({ value: tmp }, 'setting');
|
|
||||||
});
|
|
||||||
const backtestResultFields: TableField[] = [
|
|
||||||
{ key: 'metric', label: 'Metric' },
|
|
||||||
{ key: 'value', label: 'Value' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const backtestsettingFields: TableField[] = [
|
|
||||||
{ key: 'setting', label: 'Setting' },
|
|
||||||
{ key: 'value', label: 'Value' },
|
|
||||||
];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -1,3 +1,28 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
import { ChartSliderPosition, Trade } from '@/types';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
timeframe: { required: true, type: String },
|
||||||
|
strategy: { required: true, type: String },
|
||||||
|
freqaiModel: { required: false, default: undefined, type: String },
|
||||||
|
timerange: { required: true, type: String },
|
||||||
|
pairlist: { required: true, type: Array as () => string[] },
|
||||||
|
trades: { required: true, type: Array as () => Trade[] },
|
||||||
|
});
|
||||||
|
const botStore = useBotStore();
|
||||||
|
const isBarVisible = ref({ right: true, left: true });
|
||||||
|
const sliderPosition = ref<ChartSliderPosition>();
|
||||||
|
|
||||||
|
const navigateChartToTrade = (trade: Trade) => {
|
||||||
|
sliderPosition.value = {
|
||||||
|
startValue: trade.open_timestamp,
|
||||||
|
endValue: trade.close_timestamp,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="d-flex flex-row mb-1 align-items-center">
|
<div class="d-flex flex-row mb-1 align-items-center">
|
||||||
|
@ -70,31 +95,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
|
|
||||||
import { ChartSliderPosition, Trade } from '@/types';
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
timeframe: { required: true, type: String },
|
|
||||||
strategy: { required: true, type: String },
|
|
||||||
freqaiModel: { required: false, default: undefined, type: String },
|
|
||||||
timerange: { required: true, type: String },
|
|
||||||
pairlist: { required: true, type: Array as () => string[] },
|
|
||||||
trades: { required: true, type: Array as () => Trade[] },
|
|
||||||
});
|
|
||||||
const botStore = useBotStore();
|
|
||||||
const isBarVisible = ref({ right: true, left: true });
|
|
||||||
const sliderPosition = ref<ChartSliderPosition>();
|
|
||||||
|
|
||||||
const navigateChartToTrade = (trade: Trade) => {
|
|
||||||
sliderPosition.value = {
|
|
||||||
startValue: trade.open_timestamp,
|
|
||||||
endValue: trade.close_timestamp,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.candle-chart-container {
|
.candle-chart-container {
|
||||||
// TODO: Rough estimate - still to fix correctly
|
// TODO: Rough estimate - still to fix correctly
|
||||||
|
|
|
@ -1,32 +1,3 @@
|
||||||
<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">
|
|
||||||
<BTable bordered :items="backtestResultStats" :fields="backtestResultFields">
|
|
||||||
<template
|
|
||||||
v-for="[key, result] in Object.entries(backtestResults)"
|
|
||||||
#[`head(${key})`]
|
|
||||||
:key="key"
|
|
||||||
>
|
|
||||||
<BacktestResultSelectEntry :backtest-result="result" />
|
|
||||||
</template>
|
|
||||||
</BTable>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { BacktestResultInMemory } from '@/types';
|
import { BacktestResultInMemory } from '@/types';
|
||||||
|
|
||||||
|
@ -56,4 +27,33 @@ const backtestResultFields = computed<TableField[]>(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
<BTable bordered :items="backtestResultStats" :fields="backtestResultFields">
|
||||||
|
<template
|
||||||
|
v-for="[key, result] in Object.entries(backtestResults)"
|
||||||
|
#[`head(${key})`]
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<BacktestResultSelectEntry :backtest-result="result" />
|
||||||
|
</template>
|
||||||
|
</BTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -1,3 +1,37 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { BacktestResultInMemory, BacktestResultUpdate } from '@/types';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
backtestHistory: {
|
||||||
|
required: true,
|
||||||
|
type: Object as () => Record<string, BacktestResultInMemory>,
|
||||||
|
},
|
||||||
|
selectedBacktestResultKey: { required: false, default: '', type: String },
|
||||||
|
canUseModify: { required: false, default: false, type: Boolean },
|
||||||
|
});
|
||||||
|
const emit = defineEmits<{
|
||||||
|
selectionChange: [value: string];
|
||||||
|
removeResult: [value: string];
|
||||||
|
updateResult: [value: BacktestResultUpdate];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const setBacktestResult = (key: string) => {
|
||||||
|
emit('selectionChange', key);
|
||||||
|
};
|
||||||
|
|
||||||
|
function confirmInput(run_id: string, result: BacktestResultInMemory) {
|
||||||
|
result.metadata.editing = !result.metadata.editing;
|
||||||
|
if (result.metadata.filename) {
|
||||||
|
emit('updateResult', {
|
||||||
|
run_id: run_id,
|
||||||
|
notes: result.metadata.notes ?? '',
|
||||||
|
filename: result.metadata.filename,
|
||||||
|
strategy: result.metadata.strategyName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container d-flex flex-column align-items-stretch">
|
<div class="container d-flex flex-column align-items-stretch">
|
||||||
<h3>Available results:</h3>
|
<h3>Available results:</h3>
|
||||||
|
@ -45,38 +79,4 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { BacktestResultInMemory, BacktestResultUpdate } from '@/types';
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
backtestHistory: {
|
|
||||||
required: true,
|
|
||||||
type: Object as () => Record<string, BacktestResultInMemory>,
|
|
||||||
},
|
|
||||||
selectedBacktestResultKey: { required: false, default: '', type: String },
|
|
||||||
canUseModify: { required: false, default: false, type: Boolean },
|
|
||||||
});
|
|
||||||
const emit = defineEmits<{
|
|
||||||
selectionChange: [value: string];
|
|
||||||
removeResult: [value: string];
|
|
||||||
updateResult: [value: BacktestResultUpdate];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const setBacktestResult = (key: string) => {
|
|
||||||
emit('selectionChange', key);
|
|
||||||
};
|
|
||||||
|
|
||||||
function confirmInput(run_id: string, result: BacktestResultInMemory) {
|
|
||||||
result.metadata.editing = !result.metadata.editing;
|
|
||||||
if (result.metadata.filename) {
|
|
||||||
emit('updateResult', {
|
|
||||||
run_id: run_id,
|
|
||||||
notes: result.metadata.notes ?? '',
|
|
||||||
filename: result.metadata.filename,
|
|
||||||
strategy: result.metadata.strategyName,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { BacktestResultInMemory } from '@/types';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
backtestResult: {
|
||||||
|
required: true,
|
||||||
|
type: Object as () => BacktestResultInMemory,
|
||||||
|
},
|
||||||
|
selectedBacktestResultKey: { required: false, default: '', type: String },
|
||||||
|
canUseModify: { required: false, default: false, type: Boolean },
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex flex-column me-2 text-start">
|
<div class="d-flex flex-column me-2 text-start">
|
||||||
<div class="fw-bold">
|
<div class="fw-bold">
|
||||||
|
@ -13,17 +26,4 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { BacktestResultInMemory } from '@/types';
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
backtestResult: {
|
|
||||||
required: true,
|
|
||||||
type: Object as () => BacktestResultInMemory,
|
|
||||||
},
|
|
||||||
selectedBacktestResultKey: { required: false, default: '', type: String },
|
|
||||||
canUseModify: { required: false, default: false, type: Boolean },
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,3 +1,55 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
import { BacktestPayload } from '@/types';
|
||||||
|
|
||||||
|
import { useBtStore } from '@/stores/btStore';
|
||||||
|
const botStore = useBotStore();
|
||||||
|
const btStore = useBtStore();
|
||||||
|
|
||||||
|
function clickBacktest() {
|
||||||
|
const btPayload: BacktestPayload = {
|
||||||
|
strategy: btStore.strategy,
|
||||||
|
timerange: btStore.timerange,
|
||||||
|
enable_protections: btStore.enableProtections,
|
||||||
|
};
|
||||||
|
const openTradesInt = parseInt(btStore.maxOpenTrades, 10);
|
||||||
|
if (openTradesInt) {
|
||||||
|
btPayload.max_open_trades = openTradesInt;
|
||||||
|
}
|
||||||
|
if (btStore.stakeAmountUnlimited) {
|
||||||
|
btPayload.stake_amount = 'unlimited';
|
||||||
|
} else {
|
||||||
|
const stakeAmountLoc = Number(btStore.stakeAmount);
|
||||||
|
if (stakeAmountLoc) {
|
||||||
|
btPayload.stake_amount = stakeAmountLoc.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startingCapitalLoc = Number(btStore.startingCapital);
|
||||||
|
if (startingCapitalLoc) {
|
||||||
|
btPayload.dry_run_wallet = startingCapitalLoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btStore.selectedTimeframe) {
|
||||||
|
btPayload.timeframe = btStore.selectedTimeframe;
|
||||||
|
}
|
||||||
|
if (btStore.selectedDetailTimeframe) {
|
||||||
|
btPayload.timeframe_detail = btStore.selectedDetailTimeframe;
|
||||||
|
}
|
||||||
|
if (!btStore.allowCache) {
|
||||||
|
btPayload.backtest_cache = 'none';
|
||||||
|
}
|
||||||
|
if (btStore.freqAI.enabled) {
|
||||||
|
btPayload.freqaimodel = btStore.freqAI.model;
|
||||||
|
if (btStore.freqAI.identifier !== '') {
|
||||||
|
btPayload.freqai = { identifier: btStore.freqAI.identifier };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
botStore.activeBot.startBacktest(btPayload);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<span>Strategy</span>
|
<span>Strategy</span>
|
||||||
|
@ -199,56 +251,4 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
import { BacktestPayload } from '@/types';
|
|
||||||
|
|
||||||
import { useBtStore } from '@/stores/btStore';
|
|
||||||
const botStore = useBotStore();
|
|
||||||
const btStore = useBtStore();
|
|
||||||
|
|
||||||
function clickBacktest() {
|
|
||||||
const btPayload: BacktestPayload = {
|
|
||||||
strategy: btStore.strategy,
|
|
||||||
timerange: btStore.timerange,
|
|
||||||
enable_protections: btStore.enableProtections,
|
|
||||||
};
|
|
||||||
const openTradesInt = parseInt(btStore.maxOpenTrades, 10);
|
|
||||||
if (openTradesInt) {
|
|
||||||
btPayload.max_open_trades = openTradesInt;
|
|
||||||
}
|
|
||||||
if (btStore.stakeAmountUnlimited) {
|
|
||||||
btPayload.stake_amount = 'unlimited';
|
|
||||||
} else {
|
|
||||||
const stakeAmountLoc = Number(btStore.stakeAmount);
|
|
||||||
if (stakeAmountLoc) {
|
|
||||||
btPayload.stake_amount = stakeAmountLoc.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const startingCapitalLoc = Number(btStore.startingCapital);
|
|
||||||
if (startingCapitalLoc) {
|
|
||||||
btPayload.dry_run_wallet = startingCapitalLoc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btStore.selectedTimeframe) {
|
|
||||||
btPayload.timeframe = btStore.selectedTimeframe;
|
|
||||||
}
|
|
||||||
if (btStore.selectedDetailTimeframe) {
|
|
||||||
btPayload.timeframe_detail = btStore.selectedDetailTimeframe;
|
|
||||||
}
|
|
||||||
if (!btStore.allowCache) {
|
|
||||||
btPayload.backtest_cache = 'none';
|
|
||||||
}
|
|
||||||
if (btStore.freqAI.enabled) {
|
|
||||||
btPayload.freqaimodel = btStore.freqAI.model;
|
|
||||||
if (btStore.freqAI.identifier !== '') {
|
|
||||||
btPayload.freqai = { identifier: btStore.freqAI.identifier };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
botStore.activeBot.startBacktest(btPayload);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,65 +1,3 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<label class="me-auto h3">Balance</label>
|
|
||||||
<div class="float-end d-flex flex-row">
|
|
||||||
<BButton
|
|
||||||
v-if="canUseBotBalance"
|
|
||||||
size="sm"
|
|
||||||
:title="!showBotOnly ? 'Showing Account balance' : 'Showing Bot balance'"
|
|
||||||
@click="showBotOnly = !showBotOnly"
|
|
||||||
>
|
|
||||||
<i-mdi-robot v-if="showBotOnly" />
|
|
||||||
<i-mdi-bank v-else />
|
|
||||||
</BButton>
|
|
||||||
<BButton
|
|
||||||
size="sm"
|
|
||||||
:title="!hideSmallBalances ? 'Hide small balances' : 'Show all balances'"
|
|
||||||
@click="hideSmallBalances = !hideSmallBalances"
|
|
||||||
>
|
|
||||||
<i-mdi-eye-off v-if="hideSmallBalances" />
|
|
||||||
<i-mdi-eye v-else />
|
|
||||||
</BButton>
|
|
||||||
|
|
||||||
<BButton class="float-end" size="sm" @click="refreshBalance">
|
|
||||||
<i-mdi-refresh />
|
|
||||||
</BButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<BalanceChart v-if="balanceCurrencies" :currencies="chartValues" />
|
|
||||||
<div>
|
|
||||||
<p v-if="botStore.activeBot.balance.note">
|
|
||||||
<strong>{{ botStore.activeBot.balance.note }}</strong>
|
|
||||||
</p>
|
|
||||||
<BTable class="table-sm" :items="balanceCurrencies" :fields="tableFields">
|
|
||||||
<template #custom-foot>
|
|
||||||
<td class="pt-1"><strong>Total</strong></td>
|
|
||||||
<td class="pt-1">
|
|
||||||
<span
|
|
||||||
class="font-italic"
|
|
||||||
:title="`Increase over initial capital of ${formatCurrency(
|
|
||||||
botStore.activeBot.balance.starting_capital,
|
|
||||||
)} ${botStore.activeBot.balance.stake}`"
|
|
||||||
>
|
|
||||||
{{ formatPercent(botStore.activeBot.balance.starting_capital_ratio) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<!-- this is a computed prop that adds up all the expenses in the visible rows -->
|
|
||||||
<td class="pt-1">
|
|
||||||
<strong>
|
|
||||||
{{
|
|
||||||
showBotOnly && canUseBotBalance
|
|
||||||
? formatCurrency(botStore.activeBot.balance.total_bot)
|
|
||||||
: formatCurrency(botStore.activeBot.balance.total)
|
|
||||||
}}
|
|
||||||
</strong>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</BTable>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
import { BalanceValues } from '@/types';
|
import { BalanceValues } from '@/types';
|
||||||
|
@ -132,3 +70,65 @@ onMounted(() => {
|
||||||
refreshBalance();
|
refreshBalance();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="me-auto h3">Balance</label>
|
||||||
|
<div class="float-end d-flex flex-row">
|
||||||
|
<BButton
|
||||||
|
v-if="canUseBotBalance"
|
||||||
|
size="sm"
|
||||||
|
:title="!showBotOnly ? 'Showing Account balance' : 'Showing Bot balance'"
|
||||||
|
@click="showBotOnly = !showBotOnly"
|
||||||
|
>
|
||||||
|
<i-mdi-robot v-if="showBotOnly" />
|
||||||
|
<i-mdi-bank v-else />
|
||||||
|
</BButton>
|
||||||
|
<BButton
|
||||||
|
size="sm"
|
||||||
|
:title="!hideSmallBalances ? 'Hide small balances' : 'Show all balances'"
|
||||||
|
@click="hideSmallBalances = !hideSmallBalances"
|
||||||
|
>
|
||||||
|
<i-mdi-eye-off v-if="hideSmallBalances" />
|
||||||
|
<i-mdi-eye v-else />
|
||||||
|
</BButton>
|
||||||
|
|
||||||
|
<BButton class="float-end" size="sm" @click="refreshBalance">
|
||||||
|
<i-mdi-refresh />
|
||||||
|
</BButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BalanceChart v-if="balanceCurrencies" :currencies="chartValues" />
|
||||||
|
<div>
|
||||||
|
<p v-if="botStore.activeBot.balance.note">
|
||||||
|
<strong>{{ botStore.activeBot.balance.note }}</strong>
|
||||||
|
</p>
|
||||||
|
<BTable class="table-sm" :items="balanceCurrencies" :fields="tableFields">
|
||||||
|
<template #custom-foot>
|
||||||
|
<td class="pt-1"><strong>Total</strong></td>
|
||||||
|
<td class="pt-1">
|
||||||
|
<span
|
||||||
|
class="font-italic"
|
||||||
|
:title="`Increase over initial capital of ${formatCurrency(
|
||||||
|
botStore.activeBot.balance.starting_capital,
|
||||||
|
)} ${botStore.activeBot.balance.stake}`"
|
||||||
|
>
|
||||||
|
{{ formatPercent(botStore.activeBot.balance.starting_capital_ratio) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<!-- this is a computed prop that adds up all the expenses in the visible rows -->
|
||||||
|
<td class="pt-1">
|
||||||
|
<strong>
|
||||||
|
{{
|
||||||
|
showBotOnly && canUseBotBalance
|
||||||
|
? formatCurrency(botStore.activeBot.balance.total_bot)
|
||||||
|
: formatCurrency(botStore.activeBot.balance.total)
|
||||||
|
}}
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
</BTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1,76 +1,3 @@
|
||||||
<template>
|
|
||||||
<BTable
|
|
||||||
ref="tradesTable"
|
|
||||||
small
|
|
||||||
hover
|
|
||||||
show-empty
|
|
||||||
primary-key="botId"
|
|
||||||
:items="tableItems"
|
|
||||||
:fields="tableFields"
|
|
||||||
>
|
|
||||||
<template #cell(botName)="{ item, value }">
|
|
||||||
<div class="d-flex flex-row">
|
|
||||||
<BFormCheckbox
|
|
||||||
v-if="item.botId && botStore.botCount > 1"
|
|
||||||
v-model="
|
|
||||||
botStore.botStores[(item as unknown as ComparisonTableItems).botId ?? ''].isSelected
|
|
||||||
"
|
|
||||||
title="Show this bot in Dashboard"
|
|
||||||
>{{ value }}</BFormCheckbox
|
|
||||||
>
|
|
||||||
<BFormCheckbox
|
|
||||||
v-if="!item.botId && botStore.botCount > 1"
|
|
||||||
v-model="allToggled"
|
|
||||||
title="Toggle all bots"
|
|
||||||
>{{ value }}</BFormCheckbox
|
|
||||||
>
|
|
||||||
<span v-if="botStore.botCount <= 1">{{ value }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #cell(profitOpen)="{ item }">
|
|
||||||
<ProfitPill
|
|
||||||
v-if="item.profitOpen && item.botId != 'Summary'"
|
|
||||||
:profit-ratio="(item as unknown as ComparisonTableItems).profitOpenRatio"
|
|
||||||
:profit-abs="(item as unknown as ComparisonTableItems).profitOpen"
|
|
||||||
:profit-desc="`Total Profit (Open and realized) ${formatPercent(
|
|
||||||
(item as unknown as ComparisonTableItems).profitOpenRatio ?? 0.0,
|
|
||||||
)}`"
|
|
||||||
:stake-currency="(item as unknown as ComparisonTableItems).stakeCurrency"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #cell(profitClosed)="{ item }">
|
|
||||||
<ProfitPill
|
|
||||||
v-if="item.profitClosed && item.botId != 'Summary'"
|
|
||||||
:profit-ratio="(item as unknown as ComparisonTableItems).profitClosedRatio"
|
|
||||||
:profit-abs="(item as unknown as ComparisonTableItems).profitClosed"
|
|
||||||
:stake-currency="(item as unknown as ComparisonTableItems).stakeCurrency"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #cell(balance)="{ item }">
|
|
||||||
<div v-if="item.balance">
|
|
||||||
<span :title="(item as unknown as ComparisonTableItems).stakeCurrency"
|
|
||||||
>{{
|
|
||||||
formatPrice(
|
|
||||||
(item as unknown as ComparisonTableItems).balance ?? 0,
|
|
||||||
(item as unknown as ComparisonTableItems).stakeCurrencyDecimals,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
<span class="text-small">{{
|
|
||||||
` ${item.stakeCurrency}${item.isDryRun ? ' (dry)' : ''}`
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #cell(winVsLoss)="{ item }">
|
|
||||||
<div v-if="item.losses !== undefined">
|
|
||||||
<span class="text-profit">{{ item.wins }}</span> /
|
|
||||||
<span class="text-loss">{{ item.losses }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</BTable>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
import { ProfitInterface, ComparisonTableItems } from '@/types';
|
import { ProfitInterface, ComparisonTableItems } from '@/types';
|
||||||
|
@ -159,4 +86,77 @@ const tableItems = computed<TableItem[]>(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BTable
|
||||||
|
ref="tradesTable"
|
||||||
|
small
|
||||||
|
hover
|
||||||
|
show-empty
|
||||||
|
primary-key="botId"
|
||||||
|
:items="tableItems"
|
||||||
|
:fields="tableFields"
|
||||||
|
>
|
||||||
|
<template #cell(botName)="{ item, value }">
|
||||||
|
<div class="d-flex flex-row">
|
||||||
|
<BFormCheckbox
|
||||||
|
v-if="item.botId && botStore.botCount > 1"
|
||||||
|
v-model="
|
||||||
|
botStore.botStores[(item as unknown as ComparisonTableItems).botId ?? ''].isSelected
|
||||||
|
"
|
||||||
|
title="Show this bot in Dashboard"
|
||||||
|
>{{ value }}</BFormCheckbox
|
||||||
|
>
|
||||||
|
<BFormCheckbox
|
||||||
|
v-if="!item.botId && botStore.botCount > 1"
|
||||||
|
v-model="allToggled"
|
||||||
|
title="Toggle all bots"
|
||||||
|
>{{ value }}</BFormCheckbox
|
||||||
|
>
|
||||||
|
<span v-if="botStore.botCount <= 1">{{ value }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #cell(profitOpen)="{ item }">
|
||||||
|
<ProfitPill
|
||||||
|
v-if="item.profitOpen && item.botId != 'Summary'"
|
||||||
|
:profit-ratio="(item as unknown as ComparisonTableItems).profitOpenRatio"
|
||||||
|
:profit-abs="(item as unknown as ComparisonTableItems).profitOpen"
|
||||||
|
:profit-desc="`Total Profit (Open and realized) ${formatPercent(
|
||||||
|
(item as unknown as ComparisonTableItems).profitOpenRatio ?? 0.0,
|
||||||
|
)}`"
|
||||||
|
:stake-currency="(item as unknown as ComparisonTableItems).stakeCurrency"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #cell(profitClosed)="{ item }">
|
||||||
|
<ProfitPill
|
||||||
|
v-if="item.profitClosed && item.botId != 'Summary'"
|
||||||
|
:profit-ratio="(item as unknown as ComparisonTableItems).profitClosedRatio"
|
||||||
|
:profit-abs="(item as unknown as ComparisonTableItems).profitClosed"
|
||||||
|
:stake-currency="(item as unknown as ComparisonTableItems).stakeCurrency"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #cell(balance)="{ item }">
|
||||||
|
<div v-if="item.balance">
|
||||||
|
<span :title="(item as unknown as ComparisonTableItems).stakeCurrency"
|
||||||
|
>{{
|
||||||
|
formatPrice(
|
||||||
|
(item as unknown as ComparisonTableItems).balance ?? 0,
|
||||||
|
(item as unknown as ComparisonTableItems).stakeCurrencyDecimals,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span class="text-small">{{
|
||||||
|
` ${item.stakeCurrency}${item.isDryRun ? ' (dry)' : ''}`
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #cell(winVsLoss)="{ item }">
|
||||||
|
<div v-if="item.losses !== undefined">
|
||||||
|
<span class="text-profit">{{ item.wins }}</span> /
|
||||||
|
<span class="text-loss">{{ item.losses }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BTable>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,69 +1,4 @@
|
||||||
forceexit
|
forceexit
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
:disabled="!botStore.activeBot.isTrading || isRunning"
|
|
||||||
title="Start Trading"
|
|
||||||
@click="botStore.activeBot.startBot()"
|
|
||||||
>
|
|
||||||
<i-mdi-play height="24" width="24" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
:disabled="!botStore.activeBot.isTrading || !isRunning"
|
|
||||||
title="Stop Trading - Also stops handling open trades."
|
|
||||||
@click="handleStopBot()"
|
|
||||||
>
|
|
||||||
<i-mdi-stop height="24" width="24" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
:disabled="!botStore.activeBot.isTrading || !isRunning"
|
|
||||||
title="StopBuy - Stops buying, but still handles open trades"
|
|
||||||
@click="handleStopBuy()"
|
|
||||||
>
|
|
||||||
<i-mdi-pause height="24" width="24" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
:disabled="!botStore.activeBot.isTrading"
|
|
||||||
title="Reload Config - reloads configuration including strategy, resetting all settings changed on the fly."
|
|
||||||
@click="handleReloadConfig()"
|
|
||||||
>
|
|
||||||
<i-mdi-reload height="24" width="24" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
:disabled="!botStore.activeBot.isTrading"
|
|
||||||
title="Force exit all"
|
|
||||||
@click="handleForceExit()"
|
|
||||||
>
|
|
||||||
<i-mdi-close-box-multiple height="24" width="24" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="botStore.activeBot.botState && botStore.activeBot.botState.force_entry_enable"
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
:disabled="!botStore.activeBot.isTrading || !isRunning"
|
|
||||||
title="Force enter - Immediately enter a trade at an optional price. Exits are then handled according to strategy rules."
|
|
||||||
@click="forceEnter = true"
|
|
||||||
>
|
|
||||||
<i-mdi-plus-box-multiple-outline style="font-size: 20px" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="botStore.activeBot.isWebserverMode && false"
|
|
||||||
:disabled="botStore.activeBot.isTrading"
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
title="Start Trading mode"
|
|
||||||
@click="botStore.activeBot.startTrade()"
|
|
||||||
>
|
|
||||||
<i-mdi-play class="fs-4" />
|
|
||||||
</button>
|
|
||||||
<ForceEntryForm v-model="forceEnter" :pair="botStore.activeBot.selectedPair" />
|
|
||||||
<MessageBox ref="msgBox" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MessageBox, { MsgBoxObject } from '@/components/general/MessageBox.vue';
|
import MessageBox, { MsgBoxObject } from '@/components/general/MessageBox.vue';
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
@ -128,3 +63,68 @@ const handleForceExit = () => {
|
||||||
msgBox.value?.show(msg);
|
msgBox.value?.show(msg);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
:disabled="!botStore.activeBot.isTrading || isRunning"
|
||||||
|
title="Start Trading"
|
||||||
|
@click="botStore.activeBot.startBot()"
|
||||||
|
>
|
||||||
|
<i-mdi-play height="24" width="24" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
:disabled="!botStore.activeBot.isTrading || !isRunning"
|
||||||
|
title="Stop Trading - Also stops handling open trades."
|
||||||
|
@click="handleStopBot()"
|
||||||
|
>
|
||||||
|
<i-mdi-stop height="24" width="24" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
:disabled="!botStore.activeBot.isTrading || !isRunning"
|
||||||
|
title="StopBuy - Stops buying, but still handles open trades"
|
||||||
|
@click="handleStopBuy()"
|
||||||
|
>
|
||||||
|
<i-mdi-pause height="24" width="24" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
:disabled="!botStore.activeBot.isTrading"
|
||||||
|
title="Reload Config - reloads configuration including strategy, resetting all settings changed on the fly."
|
||||||
|
@click="handleReloadConfig()"
|
||||||
|
>
|
||||||
|
<i-mdi-reload height="24" width="24" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
:disabled="!botStore.activeBot.isTrading"
|
||||||
|
title="Force exit all"
|
||||||
|
@click="handleForceExit()"
|
||||||
|
>
|
||||||
|
<i-mdi-close-box-multiple height="24" width="24" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="botStore.activeBot.botState && botStore.activeBot.botState.force_entry_enable"
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
:disabled="!botStore.activeBot.isTrading || !isRunning"
|
||||||
|
title="Force enter - Immediately enter a trade at an optional price. Exits are then handled according to strategy rules."
|
||||||
|
@click="forceEnter = true"
|
||||||
|
>
|
||||||
|
<i-mdi-plus-box-multiple-outline style="font-size: 20px" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="botStore.activeBot.isWebserverMode && false"
|
||||||
|
:disabled="botStore.activeBot.isTrading"
|
||||||
|
class="btn btn-secondary btn-sm ms-1"
|
||||||
|
title="Start Trading mode"
|
||||||
|
@click="botStore.activeBot.startTrade()"
|
||||||
|
>
|
||||||
|
<i-mdi-play class="fs-4" />
|
||||||
|
</button>
|
||||||
|
<ForceEntryForm v-model="forceEnter" :pair="botStore.activeBot.selectedPair" />
|
||||||
|
<MessageBox ref="msgBox" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
<template>
|
|
||||||
<BTable class="text-start" small borderless :items="profitItems" :fields="profitFields">
|
|
||||||
<template #cell(value)="row">
|
|
||||||
<DateTimeTZ v-if="row.item.isTs && row.value" :date="row.value as number"></DateTimeTZ>
|
|
||||||
<template v-else>{{ row.value }}</template>
|
|
||||||
</template>
|
|
||||||
</BTable>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ProfitInterface } from '@/types';
|
import { ProfitInterface } from '@/types';
|
||||||
import { TableField, TableItem } from 'bootstrap-vue-next';
|
import { TableField, TableItem } from 'bootstrap-vue-next';
|
||||||
|
@ -125,3 +116,12 @@ const profitItems = computed<TableItem[]>(() => {
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BTable class="text-start" small borderless :items="profitItems" :fields="profitFields">
|
||||||
|
<template #cell(value)="row">
|
||||||
|
<DateTimeTZ v-if="row.item.isTs && row.value" :date="row.value as number"></DateTimeTZ>
|
||||||
|
<template v-else>{{ row.value }}</template>
|
||||||
|
</template>
|
||||||
|
</BTable>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
const botStore = useBotStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="botStore.activeBot.botState">
|
<div v-if="botStore.activeBot.botState">
|
||||||
<p>
|
<p>
|
||||||
|
@ -84,9 +90,3 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
|
|
||||||
const botStore = useBotStore();
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,34 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Trade } from '@/types';
|
||||||
|
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
trades: { required: true, type: Array as () => Trade[] },
|
||||||
|
title: { default: 'Trades', type: String },
|
||||||
|
stakeCurrency: { required: false, default: '', type: String },
|
||||||
|
activeTrades: { default: false, type: Boolean },
|
||||||
|
showFilter: { default: false, type: Boolean },
|
||||||
|
multiBotView: { default: false, type: Boolean },
|
||||||
|
emptyText: { default: 'No Trades to show.', type: String },
|
||||||
|
stakeCurrencyDecimals: { default: 3, type: Number },
|
||||||
|
});
|
||||||
|
const botStore = useBotStore();
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const filterText = ref('');
|
||||||
|
const perPage = props.activeTrades ? 200 : 25;
|
||||||
|
|
||||||
|
const rows = computed(() => props.trades.length);
|
||||||
|
|
||||||
|
const filteredTrades = computed(() => {
|
||||||
|
return props.trades.slice((currentPage.value - 1) * perPage, currentPage.value * perPage);
|
||||||
|
});
|
||||||
|
|
||||||
|
const tradeClick = (trade) => {
|
||||||
|
botStore.activeBot.setDetailTrade(trade);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-100 overflow-auto p-1">
|
<div class="h-100 overflow-auto p-1">
|
||||||
<BListGroup id="tradeList">
|
<BListGroup id="tradeList">
|
||||||
|
@ -33,37 +64,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { Trade } from '@/types';
|
|
||||||
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
trades: { required: true, type: Array as () => Trade[] },
|
|
||||||
title: { default: 'Trades', type: String },
|
|
||||||
stakeCurrency: { required: false, default: '', type: String },
|
|
||||||
activeTrades: { default: false, type: Boolean },
|
|
||||||
showFilter: { default: false, type: Boolean },
|
|
||||||
multiBotView: { default: false, type: Boolean },
|
|
||||||
emptyText: { default: 'No Trades to show.', type: String },
|
|
||||||
stakeCurrencyDecimals: { default: 3, type: Number },
|
|
||||||
});
|
|
||||||
const botStore = useBotStore();
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const filterText = ref('');
|
|
||||||
const perPage = props.activeTrades ? 200 : 25;
|
|
||||||
|
|
||||||
const rows = computed(() => props.trades.length);
|
|
||||||
|
|
||||||
const filteredTrades = computed(() => {
|
|
||||||
return props.trades.slice((currentPage.value - 1) * perPage, currentPage.value * perPage);
|
|
||||||
});
|
|
||||||
|
|
||||||
const tradeClick = (trade) => {
|
|
||||||
botStore.activeBot.setDetailTrade(trade);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.my-05 {
|
.my-05 {
|
||||||
margin-top: 0.125rem;
|
margin-top: 0.125rem;
|
||||||
|
|
|
@ -1,20 +1,3 @@
|
||||||
<template>
|
|
||||||
<div class="d-flex">
|
|
||||||
<div
|
|
||||||
class="px-1 d-flex flex-row flex-fill text-start justify-content-between align-items-center"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<span class="me-1 fw-bold">{{ trade.pair }}</span>
|
|
||||||
<small class="text-secondary">(#{{ trade.trade_id }})</small>
|
|
||||||
</span>
|
|
||||||
<small>
|
|
||||||
<DateTimeTZ :date="trade.open_timestamp" :date-only="true" />
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<TradeProfit class="col-5" :trade="trade" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Trade } from '@/types';
|
import { Trade } from '@/types';
|
||||||
import TradeProfit from './TradeProfit.vue';
|
import TradeProfit from './TradeProfit.vue';
|
||||||
|
@ -35,6 +18,23 @@ defineProps({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div
|
||||||
|
class="px-1 d-flex flex-row flex-fill text-start justify-content-between align-items-center"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<span class="me-1 fw-bold">{{ trade.pair }}</span>
|
||||||
|
<small class="text-secondary">(#{{ trade.trade_id }})</small>
|
||||||
|
</span>
|
||||||
|
<small>
|
||||||
|
<DateTimeTZ :date="trade.open_timestamp" :date-only="true" />
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<TradeProfit class="col-5" :trade="trade" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.card-body {
|
.card-body {
|
||||||
padding: 0 0.2em;
|
padding: 0 0.2em;
|
||||||
|
|
|
@ -1,26 +1,3 @@
|
||||||
<template>
|
|
||||||
<div class="w-100 d-flex">
|
|
||||||
<BFormSelect
|
|
||||||
id="exchange-select"
|
|
||||||
v-model="exchangeModel.exchange"
|
|
||||||
size="sm"
|
|
||||||
:options="exchangeList"
|
|
||||||
>
|
|
||||||
</BFormSelect>
|
|
||||||
<BFormSelect
|
|
||||||
id="tradeMode-select"
|
|
||||||
v-model="exchangeModel.trade_mode"
|
|
||||||
size="sm"
|
|
||||||
:options="tradeModes"
|
|
||||||
:disabled="tradeModes.length < 2"
|
|
||||||
>
|
|
||||||
</BFormSelect>
|
|
||||||
<BButton class="ms-2 no-min-w" size="sm" @click="botStore.activeBot.getExchangeList">
|
|
||||||
<i-mdi-refresh />
|
|
||||||
</BButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
@ -78,3 +55,26 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-100 d-flex">
|
||||||
|
<BFormSelect
|
||||||
|
id="exchange-select"
|
||||||
|
v-model="exchangeModel.exchange"
|
||||||
|
size="sm"
|
||||||
|
:options="exchangeList"
|
||||||
|
>
|
||||||
|
</BFormSelect>
|
||||||
|
<BFormSelect
|
||||||
|
id="tradeMode-select"
|
||||||
|
v-model="exchangeModel.trade_mode"
|
||||||
|
size="sm"
|
||||||
|
:options="tradeModes"
|
||||||
|
:disabled="tradeModes.length < 2"
|
||||||
|
>
|
||||||
|
</BFormSelect>
|
||||||
|
<BButton class="ms-2 no-min-w" size="sm" @click="botStore.activeBot.getExchangeList">
|
||||||
|
<i-mdi-refresh />
|
||||||
|
</BButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1,3 +1,92 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
import { ForceEnterPayload, OrderSides } from '@/types';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
pair: { type: String, default: '' },
|
||||||
|
positionIncrease: { type: Boolean, default: false },
|
||||||
|
});
|
||||||
|
const model = defineModel<boolean>();
|
||||||
|
const botStore = useBotStore();
|
||||||
|
|
||||||
|
const form = ref<HTMLFormElement>();
|
||||||
|
const selectedPair = ref('');
|
||||||
|
const price = ref<number | undefined>(undefined);
|
||||||
|
const stakeAmount = ref<number | undefined>(undefined);
|
||||||
|
const leverage = ref<number | undefined>(undefined);
|
||||||
|
|
||||||
|
const ordertype = ref('');
|
||||||
|
const orderSide = ref<OrderSides>(OrderSides.long);
|
||||||
|
const enterTag = ref('force_entry');
|
||||||
|
|
||||||
|
const orderTypeOptions = [
|
||||||
|
{ value: 'market', text: 'Market' },
|
||||||
|
{ value: 'limit', text: 'Limit' },
|
||||||
|
];
|
||||||
|
const orderSideOptions = [
|
||||||
|
{ value: 'long', text: 'Long' },
|
||||||
|
{ value: 'short', text: 'Short' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const checkFormValidity = () => {
|
||||||
|
const valid = form.value?.checkValidity();
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
// Exit when the form isn't valid
|
||||||
|
if (!checkFormValidity()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call forceentry
|
||||||
|
const payload: ForceEnterPayload = { pair: selectedPair.value };
|
||||||
|
if (price.value) {
|
||||||
|
payload.price = Number(price.value);
|
||||||
|
}
|
||||||
|
if (ordertype.value) {
|
||||||
|
payload.ordertype = ordertype.value;
|
||||||
|
}
|
||||||
|
if (stakeAmount.value) {
|
||||||
|
payload.stakeamount = stakeAmount.value;
|
||||||
|
}
|
||||||
|
if (botStore.activeBot.botApiVersion >= 2.13 && botStore.activeBot.shortAllowed) {
|
||||||
|
payload.side = orderSide.value;
|
||||||
|
}
|
||||||
|
if (botStore.activeBot.botApiVersion >= 2.16 && enterTag.value) {
|
||||||
|
payload.entry_tag = enterTag.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leverage.value) {
|
||||||
|
payload.leverage = leverage.value;
|
||||||
|
}
|
||||||
|
botStore.activeBot.forceentry(payload);
|
||||||
|
await nextTick();
|
||||||
|
model.value = false;
|
||||||
|
};
|
||||||
|
const resetForm = () => {
|
||||||
|
console.log('resetForm');
|
||||||
|
selectedPair.value = props.pair;
|
||||||
|
price.value = undefined;
|
||||||
|
stakeAmount.value = undefined;
|
||||||
|
ordertype.value =
|
||||||
|
botStore.activeBot.botState?.order_types?.forcebuy ||
|
||||||
|
botStore.activeBot.botState?.order_types?.force_entry ||
|
||||||
|
botStore.activeBot.botState?.order_types?.buy ||
|
||||||
|
botStore.activeBot.botState?.order_types?.entry ||
|
||||||
|
'limit';
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEntry = () => {
|
||||||
|
// Trigger submit handler
|
||||||
|
handleSubmit();
|
||||||
|
};
|
||||||
|
const inputSelect = (bvModalEvt) => {
|
||||||
|
bvModalEvt.srcElement?.select();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BModal
|
<BModal
|
||||||
id="forceentry-modal"
|
id="forceentry-modal"
|
||||||
|
@ -117,92 +206,3 @@
|
||||||
</form>
|
</form>
|
||||||
</BModal>
|
</BModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
import { ForceEnterPayload, OrderSides } from '@/types';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
pair: { type: String, default: '' },
|
|
||||||
positionIncrease: { type: Boolean, default: false },
|
|
||||||
});
|
|
||||||
const model = defineModel<boolean>();
|
|
||||||
const botStore = useBotStore();
|
|
||||||
|
|
||||||
const form = ref<HTMLFormElement>();
|
|
||||||
const selectedPair = ref('');
|
|
||||||
const price = ref<number | undefined>(undefined);
|
|
||||||
const stakeAmount = ref<number | undefined>(undefined);
|
|
||||||
const leverage = ref<number | undefined>(undefined);
|
|
||||||
|
|
||||||
const ordertype = ref('');
|
|
||||||
const orderSide = ref<OrderSides>(OrderSides.long);
|
|
||||||
const enterTag = ref('force_entry');
|
|
||||||
|
|
||||||
const orderTypeOptions = [
|
|
||||||
{ value: 'market', text: 'Market' },
|
|
||||||
{ value: 'limit', text: 'Limit' },
|
|
||||||
];
|
|
||||||
const orderSideOptions = [
|
|
||||||
{ value: 'long', text: 'Long' },
|
|
||||||
{ value: 'short', text: 'Short' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const checkFormValidity = () => {
|
|
||||||
const valid = form.value?.checkValidity();
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// Exit when the form isn't valid
|
|
||||||
if (!checkFormValidity()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// call forceentry
|
|
||||||
const payload: ForceEnterPayload = { pair: selectedPair.value };
|
|
||||||
if (price.value) {
|
|
||||||
payload.price = Number(price.value);
|
|
||||||
}
|
|
||||||
if (ordertype.value) {
|
|
||||||
payload.ordertype = ordertype.value;
|
|
||||||
}
|
|
||||||
if (stakeAmount.value) {
|
|
||||||
payload.stakeamount = stakeAmount.value;
|
|
||||||
}
|
|
||||||
if (botStore.activeBot.botApiVersion >= 2.13 && botStore.activeBot.shortAllowed) {
|
|
||||||
payload.side = orderSide.value;
|
|
||||||
}
|
|
||||||
if (botStore.activeBot.botApiVersion >= 2.16 && enterTag.value) {
|
|
||||||
payload.entry_tag = enterTag.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leverage.value) {
|
|
||||||
payload.leverage = leverage.value;
|
|
||||||
}
|
|
||||||
botStore.activeBot.forceentry(payload);
|
|
||||||
await nextTick();
|
|
||||||
model.value = false;
|
|
||||||
};
|
|
||||||
const resetForm = () => {
|
|
||||||
console.log('resetForm');
|
|
||||||
selectedPair.value = props.pair;
|
|
||||||
price.value = undefined;
|
|
||||||
stakeAmount.value = undefined;
|
|
||||||
ordertype.value =
|
|
||||||
botStore.activeBot.botState?.order_types?.forcebuy ||
|
|
||||||
botStore.activeBot.botState?.order_types?.force_entry ||
|
|
||||||
botStore.activeBot.botState?.order_types?.buy ||
|
|
||||||
botStore.activeBot.botState?.order_types?.entry ||
|
|
||||||
'limit';
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEntry = () => {
|
|
||||||
// Trigger submit handler
|
|
||||||
handleSubmit();
|
|
||||||
};
|
|
||||||
const inputSelect = (bvModalEvt) => {
|
|
||||||
bvModalEvt.srcElement?.select();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,58 +1,3 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<BModal
|
|
||||||
id="forceexit-modal"
|
|
||||||
v-model="model"
|
|
||||||
title="Force exiting a trade"
|
|
||||||
@show="resetForm"
|
|
||||||
@hidden="resetForm"
|
|
||||||
@ok="handleEntry"
|
|
||||||
>
|
|
||||||
<form ref="form" @submit.stop.prevent="handleSubmit">
|
|
||||||
<p>
|
|
||||||
<span>Exiting Trade #{{ trade.trade_id }} {{ trade.pair }}.</span>
|
|
||||||
<br />
|
|
||||||
<span>Currently owning {{ trade.amount }} {{ trade.base_currency }}</span>
|
|
||||||
</p>
|
|
||||||
<BFormGroup
|
|
||||||
label-for="stake-input"
|
|
||||||
invalid-feedback="Amount must be empty or a positive number"
|
|
||||||
:state="amount !== undefined && amount > 0"
|
|
||||||
>
|
|
||||||
<template #label>
|
|
||||||
<span class="fst-italic">*Amount in {{ trade.base_currency }} [optional]</span>
|
|
||||||
<span class="ms-1 fst-italic">{{ amountInBase }}</span>
|
|
||||||
</template>
|
|
||||||
<BFormInput id="stake-input" v-model="amount" type="number" step="0.000001"></BFormInput>
|
|
||||||
<BFormInput
|
|
||||||
id="stake-input"
|
|
||||||
v-model="amount"
|
|
||||||
type="range"
|
|
||||||
step="0.000001"
|
|
||||||
min="0"
|
|
||||||
:max="trade.amount"
|
|
||||||
></BFormInput>
|
|
||||||
</BFormGroup>
|
|
||||||
|
|
||||||
<BFormGroup
|
|
||||||
label="*OrderType"
|
|
||||||
label-for="ordertype-input"
|
|
||||||
invalid-feedback="OrderType"
|
|
||||||
:state="ordertype !== undefined"
|
|
||||||
>
|
|
||||||
<BFormSelect
|
|
||||||
v-model="ordertype"
|
|
||||||
:options="['market', 'limit']"
|
|
||||||
style="min-width: 7em"
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
</BFormSelect>
|
|
||||||
</BFormGroup>
|
|
||||||
</form>
|
|
||||||
</BModal>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
import { ForceSellPayload, Trade } from '@/types';
|
import { ForceSellPayload, Trade } from '@/types';
|
||||||
|
@ -121,3 +66,58 @@ const amountInBase = computed<string>(() => {
|
||||||
: '';
|
: '';
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<BModal
|
||||||
|
id="forceexit-modal"
|
||||||
|
v-model="model"
|
||||||
|
title="Force exiting a trade"
|
||||||
|
@show="resetForm"
|
||||||
|
@hidden="resetForm"
|
||||||
|
@ok="handleEntry"
|
||||||
|
>
|
||||||
|
<form ref="form" @submit.stop.prevent="handleSubmit">
|
||||||
|
<p>
|
||||||
|
<span>Exiting Trade #{{ trade.trade_id }} {{ trade.pair }}.</span>
|
||||||
|
<br />
|
||||||
|
<span>Currently owning {{ trade.amount }} {{ trade.base_currency }}</span>
|
||||||
|
</p>
|
||||||
|
<BFormGroup
|
||||||
|
label-for="stake-input"
|
||||||
|
invalid-feedback="Amount must be empty or a positive number"
|
||||||
|
:state="amount !== undefined && amount > 0"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span class="fst-italic">*Amount in {{ trade.base_currency }} [optional]</span>
|
||||||
|
<span class="ms-1 fst-italic">{{ amountInBase }}</span>
|
||||||
|
</template>
|
||||||
|
<BFormInput id="stake-input" v-model="amount" type="number" step="0.000001"></BFormInput>
|
||||||
|
<BFormInput
|
||||||
|
id="stake-input"
|
||||||
|
v-model="amount"
|
||||||
|
type="range"
|
||||||
|
step="0.000001"
|
||||||
|
min="0"
|
||||||
|
:max="trade.amount"
|
||||||
|
></BFormInput>
|
||||||
|
</BFormGroup>
|
||||||
|
|
||||||
|
<BFormGroup
|
||||||
|
label="*OrderType"
|
||||||
|
label-for="ordertype-input"
|
||||||
|
invalid-feedback="OrderType"
|
||||||
|
:state="ordertype !== undefined"
|
||||||
|
>
|
||||||
|
<BFormSelect
|
||||||
|
v-model="ordertype"
|
||||||
|
:options="['market', 'limit']"
|
||||||
|
style="min-width: 7em"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
</BFormSelect>
|
||||||
|
</BFormGroup>
|
||||||
|
</form>
|
||||||
|
</BModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
const locFreqaiModel = defineModel<string>();
|
||||||
|
const botStore = useBotStore();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (botStore.activeBot.freqaiModelList.length === 0) {
|
||||||
|
botStore.activeBot.getFreqAIModelList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="w-100 d-flex">
|
<div class="w-100 d-flex">
|
||||||
|
@ -15,16 +28,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
|
|
||||||
const locFreqaiModel = defineModel<string>();
|
|
||||||
const botStore = useBotStore();
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (botStore.activeBot.freqaiModelList.length === 0) {
|
|
||||||
botStore.activeBot.getFreqAIModelList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,25 +1,3 @@
|
||||||
<template>
|
|
||||||
<div class="d-flex h-100 p-0 align-items-start">
|
|
||||||
<div ref="scrollContainer" class="border p-1 text-start pb-5 w-100 h-100 overflow-auto">
|
|
||||||
<pre
|
|
||||||
v-for="(log, index) in botStore.activeBot.lastLogs"
|
|
||||||
:key="index"
|
|
||||||
class="m-0 overflow-visible"
|
|
||||||
style="line-height: unset"
|
|
||||||
><span class="text-muted">{{ log[0] }} <span :class="getLogColor(log[3])">{{ log[3].padEnd(7, ' ') }}</span> {{ log[2] }} - </span><span class="text-{{ log[1] }}">{{ log[4] }}</span
|
|
||||||
></pre>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex flex-column gap-1 ms-1">
|
|
||||||
<BButton id="refresh-logs" size="sm" title="Reload Logs" @click="refreshLogs">
|
|
||||||
<i-mdi-refresh />
|
|
||||||
</BButton>
|
|
||||||
<BButton size="sm" title="Scroll to bottom" @click="scrollToBottom">
|
|
||||||
<i-mdi-arrow-down-thick />
|
|
||||||
</BButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
@ -53,6 +31,28 @@ function scrollToBottom() {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex h-100 p-0 align-items-start">
|
||||||
|
<div ref="scrollContainer" class="border p-1 text-start pb-5 w-100 h-100 overflow-auto">
|
||||||
|
<pre
|
||||||
|
v-for="(log, index) in botStore.activeBot.lastLogs"
|
||||||
|
:key="index"
|
||||||
|
class="m-0 overflow-visible"
|
||||||
|
style="line-height: unset"
|
||||||
|
><span class="text-muted">{{ log[0] }} <span :class="getLogColor(log[3])">{{ log[3].padEnd(7, ' ') }}</span> {{ log[2] }} - </span><span class="text-{{ log[1] }}">{{ log[4] }}</span
|
||||||
|
></pre>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column gap-1 ms-1">
|
||||||
|
<BButton id="refresh-logs" size="sm" title="Reload Logs" @click="refreshLogs">
|
||||||
|
<i-mdi-refresh />
|
||||||
|
</BButton>
|
||||||
|
<BButton size="sm" title="Scroll to bottom" @click="scrollToBottom">
|
||||||
|
<i-mdi-arrow-down-thick />
|
||||||
|
</BButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,3 +1,56 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
|
||||||
|
const newblacklistpair = ref('');
|
||||||
|
const blackListShow = ref(false);
|
||||||
|
const blacklistSelect = ref<number[]>([]);
|
||||||
|
const botStore = useBotStore();
|
||||||
|
|
||||||
|
const initBlacklist = () => {
|
||||||
|
if (botStore.activeBot.whitelist.length === 0) {
|
||||||
|
botStore.activeBot.getWhitelist();
|
||||||
|
}
|
||||||
|
if (botStore.activeBot.blacklist.length === 0) {
|
||||||
|
botStore.activeBot.getBlacklist();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addBlacklistPair = () => {
|
||||||
|
if (newblacklistpair.value) {
|
||||||
|
blackListShow.value = false;
|
||||||
|
|
||||||
|
botStore.activeBot.addBlacklist({ blacklist: [newblacklistpair.value] });
|
||||||
|
newblacklistpair.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const blacklistSelectClick = (key) => {
|
||||||
|
const index = blacklistSelect.value.indexOf(key);
|
||||||
|
if (index > -1) {
|
||||||
|
blacklistSelect.value.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
blacklistSelect.value.push(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deletePairs = () => {
|
||||||
|
if (blacklistSelect.value.length === 0) {
|
||||||
|
console.log('nothing to delete');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// const pairlist = blacklistSelect.value;
|
||||||
|
const pairlist = botStore.activeBot.blacklist.filter(
|
||||||
|
(value, index) => blacklistSelect.value.indexOf(index) > -1,
|
||||||
|
);
|
||||||
|
console.log('Deleting pairs: ', pairlist);
|
||||||
|
botStore.activeBot.deleteBlacklist(pairlist);
|
||||||
|
blacklistSelect.value = [];
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
initBlacklist();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -94,59 +147,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
|
|
||||||
const newblacklistpair = ref('');
|
|
||||||
const blackListShow = ref(false);
|
|
||||||
const blacklistSelect = ref<number[]>([]);
|
|
||||||
const botStore = useBotStore();
|
|
||||||
|
|
||||||
const initBlacklist = () => {
|
|
||||||
if (botStore.activeBot.whitelist.length === 0) {
|
|
||||||
botStore.activeBot.getWhitelist();
|
|
||||||
}
|
|
||||||
if (botStore.activeBot.blacklist.length === 0) {
|
|
||||||
botStore.activeBot.getBlacklist();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const addBlacklistPair = () => {
|
|
||||||
if (newblacklistpair.value) {
|
|
||||||
blackListShow.value = false;
|
|
||||||
|
|
||||||
botStore.activeBot.addBlacklist({ blacklist: [newblacklistpair.value] });
|
|
||||||
newblacklistpair.value = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const blacklistSelectClick = (key) => {
|
|
||||||
const index = blacklistSelect.value.indexOf(key);
|
|
||||||
if (index > -1) {
|
|
||||||
blacklistSelect.value.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
blacklistSelect.value.push(key);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deletePairs = () => {
|
|
||||||
if (blacklistSelect.value.length === 0) {
|
|
||||||
console.log('nothing to delete');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// const pairlist = blacklistSelect.value;
|
|
||||||
const pairlist = botStore.activeBot.blacklist.filter(
|
|
||||||
(value, index) => blacklistSelect.value.indexOf(index) > -1,
|
|
||||||
);
|
|
||||||
console.log('Deleting pairs: ', pairlist);
|
|
||||||
botStore.activeBot.deleteBlacklist(pairlist);
|
|
||||||
blacklistSelect.value = [];
|
|
||||||
};
|
|
||||||
onMounted(() => {
|
|
||||||
initBlacklist();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.check {
|
.check {
|
||||||
// Hidden checkbox on blacklist selection
|
// Hidden checkbox on blacklist selection
|
||||||
|
|
|
@ -1,3 +1,27 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Lock } from '@/types';
|
||||||
|
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
import { TableField } from 'bootstrap-vue-next';
|
||||||
|
const botStore = useBotStore();
|
||||||
|
|
||||||
|
const tableFields: TableField[] = [
|
||||||
|
{ key: 'pair', label: 'Pair' },
|
||||||
|
{ key: 'lock_end_timestamp', label: 'Until', formatter: (value) => timestampms(value as number) },
|
||||||
|
{ key: 'reason', label: 'Reason' },
|
||||||
|
{ key: 'actions' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const removePairLock = (item: Lock) => {
|
||||||
|
console.log(item);
|
||||||
|
if (item.id !== undefined) {
|
||||||
|
botStore.activeBot.deleteLock(item.id);
|
||||||
|
} else {
|
||||||
|
showAlert('This Freqtrade version does not support deleting locks.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
|
@ -23,28 +47,4 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { Lock } from '@/types';
|
|
||||||
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
import { TableField } from 'bootstrap-vue-next';
|
|
||||||
const botStore = useBotStore();
|
|
||||||
|
|
||||||
const tableFields: TableField[] = [
|
|
||||||
{ key: 'pair', label: 'Pair' },
|
|
||||||
{ key: 'lock_end_timestamp', label: 'Until', formatter: (value) => timestampms(value as number) },
|
|
||||||
{ key: 'reason', label: 'Reason' },
|
|
||||||
{ key: 'actions' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const removePairLock = (item: Lock) => {
|
|
||||||
console.log(item);
|
|
||||||
if (item.id !== undefined) {
|
|
||||||
botStore.activeBot.deleteLock(item.id);
|
|
||||||
} else {
|
|
||||||
showAlert('This Freqtrade version does not support deleting locks.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -1,41 +1,3 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<BFormGroup
|
|
||||||
label-for="trade-filter"
|
|
||||||
class="mb-2 ms-2"
|
|
||||||
:class="{
|
|
||||||
'me-4': backtestMode,
|
|
||||||
'me-2': !backtestMode,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<BFormInput id="trade-filter" v-model="filterText" type="text" placeholder="Filter" />
|
|
||||||
</BFormGroup>
|
|
||||||
<BListGroup>
|
|
||||||
<BListGroupItem
|
|
||||||
v-for="comb in combinedPairList"
|
|
||||||
:key="comb.pair"
|
|
||||||
button
|
|
||||||
class="d-flex justify-content-between align-items-center py-1"
|
|
||||||
:active="comb.pair === botStore.activeBot.selectedPair"
|
|
||||||
:title="`${comb.pair} - ${comb.tradeCount} trades`"
|
|
||||||
@click="botStore.activeBot.selectedPair = comb.pair"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
{{ comb.pair }}
|
|
||||||
<span v-if="comb.locks" :title="comb.lockReason"> <i-mdi-lock /> </span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TradeProfit v-if="comb.trade && !backtestMode" :trade="comb.trade" />
|
|
||||||
<ProfitPill
|
|
||||||
v-if="backtestMode && comb.tradeCount > 0"
|
|
||||||
:profit-ratio="comb.profit"
|
|
||||||
:stake-currency="botStore.activeBot.stakeCurrency"
|
|
||||||
/>
|
|
||||||
</BListGroupItem>
|
|
||||||
</BListGroup>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Lock, Trade } from '@/types';
|
import { Lock, Trade } from '@/types';
|
||||||
|
|
||||||
|
@ -131,6 +93,44 @@ const combinedPairList = computed(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<BFormGroup
|
||||||
|
label-for="trade-filter"
|
||||||
|
class="mb-2 ms-2"
|
||||||
|
:class="{
|
||||||
|
'me-4': backtestMode,
|
||||||
|
'me-2': !backtestMode,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<BFormInput id="trade-filter" v-model="filterText" type="text" placeholder="Filter" />
|
||||||
|
</BFormGroup>
|
||||||
|
<BListGroup>
|
||||||
|
<BListGroupItem
|
||||||
|
v-for="comb in combinedPairList"
|
||||||
|
:key="comb.pair"
|
||||||
|
button
|
||||||
|
class="d-flex justify-content-between align-items-center py-1"
|
||||||
|
:active="comb.pair === botStore.activeBot.selectedPair"
|
||||||
|
:title="`${comb.pair} - ${comb.tradeCount} trades`"
|
||||||
|
@click="botStore.activeBot.selectedPair = comb.pair"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ comb.pair }}
|
||||||
|
<span v-if="comb.locks" :title="comb.lockReason"> <i-mdi-lock /> </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TradeProfit v-if="comb.trade && !backtestMode" :trade="comb.trade" />
|
||||||
|
<ProfitPill
|
||||||
|
v-if="backtestMode && comb.tradeCount > 0"
|
||||||
|
:profit-ratio="comb.profit"
|
||||||
|
:stake-currency="botStore.activeBot.stakeCurrency"
|
||||||
|
/>
|
||||||
|
</BListGroupItem>
|
||||||
|
</BListGroup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.list-group {
|
.list-group {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
||||||
|
import EditValue from '../general/EditValue.vue';
|
||||||
|
const pairlistStore = usePairlistConfigStore();
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex flex-column flex-sm-row mb-2 gap-2">
|
<div class="d-flex flex-column flex-sm-row mb-2 gap-2">
|
||||||
<BButton
|
<BButton
|
||||||
|
@ -40,8 +45,3 @@
|
||||||
</BButton>
|
</BButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
|
||||||
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
|
||||||
import EditValue from '../general/EditValue.vue';
|
|
||||||
const pairlistStore = usePairlistConfigStore();
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
||||||
|
const pairlistStore = usePairlistConfigStore();
|
||||||
|
const copyFromConfig = ref('');
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
const configNames = computed(() =>
|
||||||
|
pairlistStore.savedConfigs.filter((c) => c.name !== pairlistStore.config.name).map((c) => c.name),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<BCard no-body class="mb-2">
|
<BCard no-body class="mb-2">
|
||||||
<template #header>
|
<template #header>
|
||||||
|
@ -48,14 +58,4 @@
|
||||||
</BCollapse>
|
</BCollapse>
|
||||||
</BCard>
|
</BCard>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
|
||||||
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
|
||||||
const pairlistStore = usePairlistConfigStore();
|
|
||||||
const copyFromConfig = ref('');
|
|
||||||
const visible = ref(false);
|
|
||||||
|
|
||||||
const configNames = computed(() =>
|
|
||||||
pairlistStore.savedConfigs.filter((c) => c.name !== pairlistStore.config.name).map((c) => c.name),
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -1,3 +1,24 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
||||||
|
import { Pairlist } from '@/types';
|
||||||
|
|
||||||
|
const pairlistStore = usePairlistConfigStore();
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
index: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const pairlist = defineModel<Pairlist>({ required: true });
|
||||||
|
|
||||||
|
const hasParameters = computed(() => Object.keys(pairlist.value.params).length > 0);
|
||||||
|
|
||||||
|
function toggleVisible() {
|
||||||
|
if (hasParameters.value) {
|
||||||
|
pairlist.value.showParameters = !pairlist.value.showParameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BCard no-body class="mb-2">
|
<BCard no-body class="mb-2">
|
||||||
<template #header>
|
<template #header>
|
||||||
|
@ -54,25 +75,4 @@
|
||||||
</BCard>
|
</BCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
|
||||||
import { Pairlist } from '@/types';
|
|
||||||
|
|
||||||
const pairlistStore = usePairlistConfigStore();
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
index: number;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const pairlist = defineModel<Pairlist>({ required: true });
|
|
||||||
|
|
||||||
const hasParameters = computed(() => Object.keys(pairlist.value.params).length > 0);
|
|
||||||
|
|
||||||
function toggleVisible() {
|
|
||||||
if (hasParameters.value) {
|
|
||||||
pairlist.value.showParameters = !pairlist.value.showParameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PairlistParameter, PairlistParamType } from '@/types';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
param: PairlistParameter;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// TODO: type should really be PairlistParamValue
|
||||||
|
const paramValue = defineModel<any>();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BFormGroup label-cols="4" label-size="md" class="pb-1 text-start" :description="param.help">
|
<BFormGroup label-cols="4" label-size="md" class="pb-1 text-start" :description="param.help">
|
||||||
<BFormInput
|
<BFormInput
|
||||||
|
@ -22,14 +33,3 @@
|
||||||
</template>
|
</template>
|
||||||
</BFormGroup>
|
</BFormGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { PairlistParameter, PairlistParamType } from '@/types';
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
param: PairlistParameter;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
// TODO: type should really be PairlistParamValue
|
|
||||||
const paramValue = defineModel<any>();
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
||||||
|
import ChartView from '@/views/ChartsView.vue';
|
||||||
|
|
||||||
|
const botStore = useBotStore();
|
||||||
|
const pairlistStore = usePairlistConfigStore();
|
||||||
|
|
||||||
|
const whitelist = ref<{ enabled: boolean; pair: string }[]>([]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => pairlistStore.whitelist,
|
||||||
|
() => {
|
||||||
|
whitelist.value = pairlistStore.whitelist.map((p) => {
|
||||||
|
return {
|
||||||
|
enabled: true,
|
||||||
|
pair: p,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="whitelist.length > 0" class="d-flex flex-column flex-lg-row px-2">
|
<div v-if="whitelist.length > 0" class="d-flex flex-column flex-lg-row px-2">
|
||||||
|
@ -38,26 +61,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
|
||||||
import ChartView from '@/views/ChartsView.vue';
|
|
||||||
|
|
||||||
const botStore = useBotStore();
|
|
||||||
const pairlistStore = usePairlistConfigStore();
|
|
||||||
|
|
||||||
const whitelist = ref<{ enabled: boolean; pair: string }[]>([]);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => pairlistStore.whitelist,
|
|
||||||
() => {
|
|
||||||
whitelist.value = pairlistStore.whitelist.map((p) => {
|
|
||||||
return {
|
|
||||||
enabled: true,
|
|
||||||
pair: p,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,74 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useBotStore } from '@/stores/ftbotwrapper';
|
||||||
|
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
||||||
|
import PairlistConfigItem from './PairlistConfigItem.vue';
|
||||||
|
import PairlistConfigBlacklist from './PairlistConfigBlacklist.vue';
|
||||||
|
import PairlistConfigActions from './PairlistConfigActions.vue';
|
||||||
|
import { Pairlist } from '@/types';
|
||||||
|
import { useSortable, moveArrayElement } from '@vueuse/integrations/useSortable';
|
||||||
|
import ExchangeSelect from './ExchangeSelect.vue';
|
||||||
|
|
||||||
|
const botStore = useBotStore();
|
||||||
|
const pairlistStore = usePairlistConfigStore();
|
||||||
|
|
||||||
|
const availablePairlists = ref<Pairlist[]>([]);
|
||||||
|
const pairlistConfigsEl = ref<HTMLElement | null>(null);
|
||||||
|
const availablePairlistsEl = ref<HTMLElement | null>(null);
|
||||||
|
const selectedView = ref<'Config' | 'Results'>('Config');
|
||||||
|
|
||||||
|
const configEmpty = computed(() => {
|
||||||
|
return pairlistStore.config.pairlists.length == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
useSortable(availablePairlistsEl, availablePairlists.value, {
|
||||||
|
group: {
|
||||||
|
name: 'configurator',
|
||||||
|
pull: 'clone',
|
||||||
|
put: false,
|
||||||
|
},
|
||||||
|
sort: false,
|
||||||
|
filter: '.no-drag',
|
||||||
|
dragClass: 'dragging',
|
||||||
|
});
|
||||||
|
|
||||||
|
useSortable(pairlistConfigsEl, pairlistStore.config.pairlists, {
|
||||||
|
handle: '.handle',
|
||||||
|
group: 'configurator',
|
||||||
|
onUpdate: async (e) => {
|
||||||
|
moveArrayElement(pairlistStore.config.pairlists, e.oldIndex, e.newIndex);
|
||||||
|
},
|
||||||
|
onAdd: (e) => {
|
||||||
|
const pairlist = availablePairlists.value[e.oldIndex];
|
||||||
|
pairlistStore.addToConfig(pairlist, e.newIndex);
|
||||||
|
// quick fix from: https://github.com/SortableJS/Sortable/issues/1515
|
||||||
|
e.clone.replaceWith(e.item);
|
||||||
|
e.clone.remove();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
availablePairlists.value = (await botStore.activeBot.getPairlists()).pairlists.sort((a, b) =>
|
||||||
|
// Sort by is_pairlist_generator (by name), then by name.
|
||||||
|
// TODO: this might need to be improved
|
||||||
|
a.is_pairlist_generator === b.is_pairlist_generator
|
||||||
|
? a.name.localeCompare(b.name)
|
||||||
|
: a.is_pairlist_generator
|
||||||
|
? -1
|
||||||
|
: 1,
|
||||||
|
);
|
||||||
|
pairlistStore.selectOrCreateConfig(
|
||||||
|
pairlistStore.isSavedConfig(pairlistStore.configName) ? pairlistStore.configName : 'default',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => pairlistStore.whitelist,
|
||||||
|
() => {
|
||||||
|
selectedView.value = 'Results';
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex px-3 mb-3 gap-3 flex-column flex-lg-row">
|
<div class="d-flex px-3 mb-3 gap-3 flex-column flex-lg-row">
|
||||||
<BListGroup ref="availablePairlistsEl" class="available-pairlists">
|
<BListGroup ref="availablePairlistsEl" class="available-pairlists">
|
||||||
|
@ -90,77 +161,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useBotStore } from '@/stores/ftbotwrapper';
|
|
||||||
import { usePairlistConfigStore } from '@/stores/pairlistConfig';
|
|
||||||
import PairlistConfigItem from './PairlistConfigItem.vue';
|
|
||||||
import PairlistConfigBlacklist from './PairlistConfigBlacklist.vue';
|
|
||||||
import PairlistConfigActions from './PairlistConfigActions.vue';
|
|
||||||
import { Pairlist } from '@/types';
|
|
||||||
import { useSortable, moveArrayElement } from '@vueuse/integrations/useSortable';
|
|
||||||
import ExchangeSelect from './ExchangeSelect.vue';
|
|
||||||
|
|
||||||
const botStore = useBotStore();
|
|
||||||
const pairlistStore = usePairlistConfigStore();
|
|
||||||
|
|
||||||
const availablePairlists = ref<Pairlist[]>([]);
|
|
||||||
const pairlistConfigsEl = ref<HTMLElement | null>(null);
|
|
||||||
const availablePairlistsEl = ref<HTMLElement | null>(null);
|
|
||||||
const selectedView = ref<'Config' | 'Results'>('Config');
|
|
||||||
|
|
||||||
const configEmpty = computed(() => {
|
|
||||||
return pairlistStore.config.pairlists.length == 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
useSortable(availablePairlistsEl, availablePairlists.value, {
|
|
||||||
group: {
|
|
||||||
name: 'configurator',
|
|
||||||
pull: 'clone',
|
|
||||||
put: false,
|
|
||||||
},
|
|
||||||
sort: false,
|
|
||||||
filter: '.no-drag',
|
|
||||||
dragClass: 'dragging',
|
|
||||||
});
|
|
||||||
|
|
||||||
useSortable(pairlistConfigsEl, pairlistStore.config.pairlists, {
|
|
||||||
handle: '.handle',
|
|
||||||
group: 'configurator',
|
|
||||||
onUpdate: async (e) => {
|
|
||||||
moveArrayElement(pairlistStore.config.pairlists, e.oldIndex, e.newIndex);
|
|
||||||
},
|
|
||||||
onAdd: (e) => {
|
|
||||||
const pairlist = availablePairlists.value[e.oldIndex];
|
|
||||||
pairlistStore.addToConfig(pairlist, e.newIndex);
|
|
||||||
// quick fix from: https://github.com/SortableJS/Sortable/issues/1515
|
|
||||||
e.clone.replaceWith(e.item);
|
|
||||||
e.clone.remove();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
availablePairlists.value = (await botStore.activeBot.getPairlists()).pairlists.sort((a, b) =>
|
|
||||||
// Sort by is_pairlist_generator (by name), then by name.
|
|
||||||
// TODO: this might need to be improved
|
|
||||||
a.is_pairlist_generator === b.is_pairlist_generator
|
|
||||||
? a.name.localeCompare(b.name)
|
|
||||||
: a.is_pairlist_generator
|
|
||||||
? -1
|
|
||||||
: 1,
|
|
||||||
);
|
|
||||||
pairlistStore.selectOrCreateConfig(
|
|
||||||
pairlistStore.isSavedConfig(pairlistStore.configName) ? pairlistStore.configName : 'default',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => pairlistStore.whitelist,
|
|
||||||
() => {
|
|
||||||
selectedView.value = 'Results';
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pairlist {
|
.pairlist {
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user