mirror of
https://github.com/freqtrade/frequi.git
synced 2024-11-23 11:35:14 +00:00
Merge branch 'main' into pr/Tako88/1265
This commit is contained in:
commit
af05f9fc88
34
index.html
34
index.html
|
@ -1,18 +1,22 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" data-theme="dark">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
<head>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<title>FreqUI</title>
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<title>FreqUI</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body data-bs-theme="dark">
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but FreqUI doesn't work properly without JavaScript enabled. Please enable it to
|
||||||
|
continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but FreqUI doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,8 +6,11 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
|
import { useColorMode } from 'bootstrap-vue-next';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
const mode = useColorMode();
|
||||||
|
|
||||||
const activeTheme = ref('');
|
const activeTheme = ref('');
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
||||||
|
@ -18,17 +21,14 @@ const setTheme = (themeName: string) => {
|
||||||
}
|
}
|
||||||
if (themeName.toLowerCase() === 'bootstrap' || themeName.toLowerCase() === 'bootstrap_dark') {
|
if (themeName.toLowerCase() === 'bootstrap' || themeName.toLowerCase() === 'bootstrap_dark') {
|
||||||
// const styles = document.getElementsByTagName('style');
|
// const styles = document.getElementsByTagName('style');
|
||||||
document.documentElement.setAttribute(
|
|
||||||
'data-theme',
|
|
||||||
themeName.toLowerCase() === 'bootstrap' ? 'light' : 'dark',
|
|
||||||
);
|
|
||||||
if (activeTheme.value) {
|
if (activeTheme.value) {
|
||||||
// Only transition if simple mode is active
|
// Only transition if simple mode is active
|
||||||
document.documentElement.classList.add('ft-theme-transition');
|
document.body.classList.add('ft-theme-transition');
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
document.documentElement.classList.remove('ft-theme-transition');
|
document.body.classList.remove('ft-theme-transition');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
mode.value = themeName.toLowerCase() === 'bootstrap' ? 'light' : 'dark';
|
||||||
}
|
}
|
||||||
// Save the theme as localstorage
|
// Save the theme as localstorage
|
||||||
settingsStore.currentTheme = themeName;
|
settingsStore.currentTheme = themeName;
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
v-model="plotStore.plotConfigName"
|
v-model="plotStore.plotConfigName"
|
||||||
:allow-edit="allowEdit"
|
:allow-edit="allowEdit"
|
||||||
:allow-add="allowEdit"
|
:allow-add="allowEdit"
|
||||||
|
:allow-duplicate="allowEdit"
|
||||||
editable-name="plot configuration"
|
editable-name="plot configuration"
|
||||||
@rename="plotStore.renamePlotConfig"
|
@rename="plotStore.renamePlotConfig"
|
||||||
@delete="plotStore.deletePlotConfig"
|
@delete="plotStore.deletePlotConfig"
|
||||||
@new="plotStore.newPlotConfig"
|
@new="plotStore.newPlotConfig"
|
||||||
|
@duplicate="plotStore.duplicatePlotConfig"
|
||||||
>
|
>
|
||||||
<b-form-select
|
<b-form-select
|
||||||
v-model="plotStore.plotConfigName"
|
v-model="plotStore.plotConfigName"
|
||||||
|
|
|
@ -1,22 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<slot v-if="!editing"> </slot>
|
<slot v-if="mode === EditState.None"> </slot>
|
||||||
<b-form-input v-else v-model="localName" size="sm"> </b-form-input>
|
<b-form-input v-else v-model="localName" size="sm"> </b-form-input>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex-grow-2 mt-auto d-flex gap-1 ms-1"
|
class="flex-grow-2 mt-auto d-flex gap-1 ms-1"
|
||||||
:class="alignVertical ? 'flex-column' : 'flex-row'"
|
:class="alignVertical ? 'flex-column' : 'flex-row'"
|
||||||
>
|
>
|
||||||
<template v-if="allowEdit && !(addNew || editing)">
|
<template v-if="allowEdit && mode === EditState.None">
|
||||||
<b-button
|
<b-button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
:title="`Edit this ${editableName}.`"
|
:title="`Edit this ${editableName}.`"
|
||||||
@click="editing = true"
|
@click="mode = EditState.Editing"
|
||||||
>
|
>
|
||||||
<i-mdi-pencil />
|
<i-mdi-pencil />
|
||||||
</b-button>
|
</b-button>
|
||||||
|
<b-button
|
||||||
|
v-if="allowDuplicate"
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
|
:title="`Duplicate ${editableName}.`"
|
||||||
|
@click="duplicate"
|
||||||
|
>
|
||||||
|
<i-mdi-content-copy />
|
||||||
|
</b-button>
|
||||||
<b-button
|
<b-button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
@ -27,14 +36,14 @@
|
||||||
</b-button>
|
</b-button>
|
||||||
</template>
|
</template>
|
||||||
<b-button
|
<b-button
|
||||||
v-if="allowAdd && !(addNew || editing)"
|
v-if="allowAdd && mode === EditState.None"
|
||||||
size="sm"
|
size="sm"
|
||||||
:title="`Add new ${editableName}.`"
|
:title="`Add new ${editableName}.`"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@click="addNewClick"
|
@click="addNewClick"
|
||||||
><i-mdi-plus-box-outline />
|
><i-mdi-plus-box-outline />
|
||||||
</b-button>
|
</b-button>
|
||||||
<template v-if="addNew || editing">
|
<template v-if="mode !== EditState.None">
|
||||||
<b-button
|
<b-button
|
||||||
size="sm"
|
size="sm"
|
||||||
:title="`Add new '${editableName}`"
|
:title="`Add new '${editableName}`"
|
||||||
|
@ -67,6 +76,10 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
allowDuplicate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
editableName: {
|
editableName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -78,26 +91,37 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'delete', value: string): void;
|
delete: [value: string];
|
||||||
(e: 'new', value: string): void;
|
new: [value: string];
|
||||||
(e: 'rename', oldName: string, newName: string): void;
|
duplicate: [oldName: string, newName: string];
|
||||||
|
rename: [oldName: string, newName: string];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const addNew = ref(false);
|
enum EditState {
|
||||||
|
None,
|
||||||
|
Editing,
|
||||||
|
Adding,
|
||||||
|
Duplicating,
|
||||||
|
}
|
||||||
|
|
||||||
const localName = ref<string>(props.modelValue);
|
const localName = ref<string>(props.modelValue);
|
||||||
const editing = ref<boolean>(false);
|
const mode = ref<EditState>(EditState.None);
|
||||||
|
|
||||||
function abort() {
|
function abort() {
|
||||||
editing.value = false;
|
mode.value = EditState.None;
|
||||||
addNew.value = false;
|
|
||||||
localName.value = props.modelValue;
|
localName.value = props.modelValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function duplicate() {
|
||||||
|
localName.value = localName.value + ' (copy)';
|
||||||
|
mode.value = EditState.Duplicating;
|
||||||
|
}
|
||||||
|
|
||||||
function addNewClick() {
|
function addNewClick() {
|
||||||
localName.value = '';
|
localName.value = '';
|
||||||
addNew.value = true;
|
mode.value = EditState.Adding;
|
||||||
editing.value = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
() => {
|
() => {
|
||||||
|
@ -106,13 +130,14 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
function saveNewName() {
|
function saveNewName() {
|
||||||
editing.value = false;
|
if (mode.value === EditState.Adding) {
|
||||||
if (addNew.value) {
|
|
||||||
addNew.value = false;
|
|
||||||
emit('new', localName.value);
|
emit('new', localName.value);
|
||||||
|
} else if (mode.value === EditState.Duplicating) {
|
||||||
|
emit('duplicate', props.modelValue, localName.value);
|
||||||
} else {
|
} else {
|
||||||
// Editing
|
// Editing
|
||||||
emit('rename', props.modelValue, localName.value);
|
emit('rename', props.modelValue, localName.value);
|
||||||
}
|
}
|
||||||
|
mode.value = EditState.None;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,6 +37,12 @@ import {
|
||||||
BotDescriptor,
|
BotDescriptor,
|
||||||
BgTaskStarted,
|
BgTaskStarted,
|
||||||
BackgroundTaskStatus,
|
BackgroundTaskStatus,
|
||||||
|
Exchange,
|
||||||
|
ExchangeListResult,
|
||||||
|
FreqAIModelListResult,
|
||||||
|
PairlistEvalResponse,
|
||||||
|
PairlistsPayload,
|
||||||
|
PairlistsResponse,
|
||||||
} from '@/types';
|
} from '@/types';
|
||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
@ -44,12 +50,6 @@ import { showAlert } from './alerts';
|
||||||
import { useWebSocket } from '@vueuse/core';
|
import { useWebSocket } from '@vueuse/core';
|
||||||
import { FTWsMessage, FtWsMessageTypes } from '@/types/wsMessageTypes';
|
import { FTWsMessage, FtWsMessageTypes } from '@/types/wsMessageTypes';
|
||||||
import { showNotification } from '@/shared/notifications';
|
import { showNotification } from '@/shared/notifications';
|
||||||
import {
|
|
||||||
FreqAIModelListResult,
|
|
||||||
PairlistEvalResponse,
|
|
||||||
PairlistsPayload,
|
|
||||||
PairlistsResponse,
|
|
||||||
} from '../types';
|
|
||||||
|
|
||||||
export function createBotSubStore(botId: string, botName: string) {
|
export function createBotSubStore(botId: string, botName: string) {
|
||||||
const userService = useUserService(botId);
|
const userService = useUserService(botId);
|
||||||
|
@ -91,6 +91,7 @@ export function createBotSubStore(botId: string, botName: string) {
|
||||||
strategyPlotConfig: undefined as PlotConfig | undefined,
|
strategyPlotConfig: undefined as PlotConfig | undefined,
|
||||||
strategyList: [] as string[],
|
strategyList: [] as string[],
|
||||||
freqaiModelList: [] as string[],
|
freqaiModelList: [] as string[],
|
||||||
|
exchangeList: [] as Exchange[],
|
||||||
strategy: {} as StrategyResult,
|
strategy: {} as StrategyResult,
|
||||||
pairlist: [] as string[],
|
pairlist: [] as string[],
|
||||||
currentLocks: undefined as LockResponse | undefined,
|
currentLocks: undefined as LockResponse | undefined,
|
||||||
|
@ -448,6 +449,16 @@ export function createBotSubStore(botId: string, botName: string) {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async getExchangeList() {
|
||||||
|
try {
|
||||||
|
const { data } = await api.get<ExchangeListResult>('/exchanges');
|
||||||
|
this.exchangeList = data.exchanges;
|
||||||
|
return Promise.resolve(data.exchanges);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
async getAvailablePairs(payload: AvailablePairPayload) {
|
async getAvailablePairs(payload: AvailablePairPayload) {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get<AvailablePairResult>('/available_pairs', {
|
const { data } = await api.get<AvailablePairResult>('/available_pairs', {
|
||||||
|
|
|
@ -72,6 +72,11 @@ export const usePlotConfigStore = defineStore('plotConfig', {
|
||||||
this.editablePlotConfig = deepClone(this.customPlotConfigs[this.plotConfigName]);
|
this.editablePlotConfig = deepClone(this.customPlotConfigs[this.plotConfigName]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
duplicatePlotConfig(oldName: string, newName: string) {
|
||||||
|
console.log(oldName, newName);
|
||||||
|
this.customPlotConfigs[newName] = deepClone(this.customPlotConfigs[oldName]);
|
||||||
|
this.plotConfigChanged(newName);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
persist: {
|
persist: {
|
||||||
key: FT_PLOT_CONFIG_KEY,
|
key: FT_PLOT_CONFIG_KEY,
|
||||||
|
|
|
@ -2,3 +2,6 @@
|
||||||
// $body-bg: rgb(42, 42, 49);
|
// $body-bg: rgb(42, 42, 49);
|
||||||
$font-size-base: 0.9rem;
|
$font-size-base: 0.9rem;
|
||||||
$primary: #0089a1;
|
$primary: #0089a1;
|
||||||
|
|
||||||
|
$body-bg-dark: #121212;
|
||||||
|
$body-color-dark: #dedede;
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-bs-theme="dark"] {
|
||||||
$bg-dark: rgb(18, 18, 18);
|
$bg-dark: rgb(18, 18, 18);
|
||||||
|
|
||||||
$bg-darker: darken($bg-dark, 5%);
|
$bg-darker: darken($bg-dark, 5%);
|
||||||
|
@ -235,11 +235,14 @@
|
||||||
background-color: unset !important;
|
background-color: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.ft-theme-transition,
|
body.ft-theme-transition,
|
||||||
html.ft-theme-transition *,
|
body.ft-theme-transition *,
|
||||||
html.ft-theme-transition *:before,
|
body.ft-theme-transition *:before,
|
||||||
html.ft-theme-transition *:after {
|
body.ft-theme-transition *:after {
|
||||||
transition: background 750ms ease-in-out,
|
transition:
|
||||||
border-color 750ms ease-in-out;
|
background 750ms ease-in-out,
|
||||||
|
border-color 750ms ease-in-out,
|
||||||
|
background-color 750ms ease-in-out,
|
||||||
|
;
|
||||||
transition-delay: 0 !important;
|
transition-delay: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
18
src/types/exchange.ts
Normal file
18
src/types/exchange.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { MarginMode, TradingMode } from './types';
|
||||||
|
|
||||||
|
export interface TradeMode {
|
||||||
|
trading_mode: TradingMode;
|
||||||
|
margin_mode: MarginMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Exchange {
|
||||||
|
name: string;
|
||||||
|
valid: boolean;
|
||||||
|
supported: boolean;
|
||||||
|
comment: string;
|
||||||
|
trade_modes: TradeMode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExchangeListResult {
|
||||||
|
exchanges: Exchange[];
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ export * from './balance';
|
||||||
export * from './blacklist';
|
export * from './blacklist';
|
||||||
export * from './botComparison';
|
export * from './botComparison';
|
||||||
export * from './chart';
|
export * from './chart';
|
||||||
|
export * from './exchange';
|
||||||
export * from './daily';
|
export * from './daily';
|
||||||
export * from './gridLayout';
|
export * from './gridLayout';
|
||||||
export * from './locks';
|
export * from './locks';
|
||||||
|
|
|
@ -80,6 +80,12 @@ export enum TradingMode {
|
||||||
FUTURES = 'futures',
|
FUTURES = 'futures',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum MarginMode {
|
||||||
|
NONE = 'none',
|
||||||
|
ISOLATED = 'isolated',
|
||||||
|
// CROSS = 'cross',
|
||||||
|
}
|
||||||
|
|
||||||
export interface UnfilledTimeout {
|
export interface UnfilledTimeout {
|
||||||
/** @deprecated replaced by entry in 2.x */
|
/** @deprecated replaced by entry in 2.x */
|
||||||
buy?: number;
|
buy?: number;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user