import { defineStore } from 'pinia'; import { useBotStore } from './ftbotwrapper'; import { ExchangeSelection, MarginMode, Pairlist, PairlistConfig, PairlistParamType, PairlistParamValue, PairlistPayloadItem, PairlistsPayload, TradingMode, } from '@/types'; import { computed, ref, toRaw, watch } from 'vue'; import { showAlert } from '../shared/alerts'; import { isNotUndefined } from '@/shared/formatters'; export const usePairlistConfigStore = defineStore( 'pairlistConfig', () => { const botStore = useBotStore(); const evaluating = ref(false); const intervalId = ref(); const stakeCurrency = ref(botStore.activeBot?.stakeCurrency ?? 'USDT'); const whitelist = ref([]); const customExchange = ref(false); const selectedExchange = ref({ exchange: botStore.activeBot?.botState.exchange ?? '', trade_mode: { trading_mode: botStore.activeBot?.botState.trading_mode ?? TradingMode.SPOT, margin_mode: botStore.activeBot?.botState.trading_mode === TradingMode.FUTURES ? MarginMode.ISOLATED : MarginMode.NONE, }, }); const config = ref(makeConfig()); const savedConfigs = ref([]); const configName = ref(''); const firstPairlistIsGenerator = computed(() => { // First pairlist must be a generator if (config.value.pairlists[0]?.is_pairlist_generator) { return true; } return false; }); const pairlistValid = computed(() => { return firstPairlistIsGenerator.value && config.value.pairlists.length > 0; }); const configJSON = computed(() => { return JSON.stringify(configToPayloadItems(), null, 2); }); const isSavedConfig = (name: string) => savedConfigs.value.findIndex((c) => c.name === name) > -1; function addToConfig(pairlist: Pairlist, index: number) { pairlist = structuredClone(toRaw(pairlist)); pairlist.showParameters = false; if (!pairlist.id) { pairlist.id = Date.now().toString(36) + Math.random().toString(36).substring(2); } for (const param in pairlist.params) { pairlist.params[param].value = isNotUndefined(pairlist.params[param].default) ? pairlist.params[param].default : ''; } config.value.pairlists.splice(index, 0, pairlist); } function removeFromConfig(index: number) { config.value.pairlists.splice(index, 1); } function saveConfig(name = '') { const i = savedConfigs.value.findIndex((c) => c.name === config.value.name); config.value.name = name; if (i > -1) { savedConfigs.value[i] = structuredClone(toRaw(config.value)); } else { savedConfigs.value.push(structuredClone(toRaw(config.value))); } } function newConfig(name: string) { const c = makeConfig({ name }); savedConfigs.value.push(c); config.value = structuredClone(c); } function duplicateConfig(name = '') { const c = makeConfig({ name, pairlists: toRaw(config.value.pairlists) as [], blacklist: toRaw(config.value.blacklist) as [], }); savedConfigs.value.push(c); config.value = structuredClone(c); } function deleteConfig() { const i = savedConfigs.value.findIndex((c) => c.name === config.value.name); if (i > -1) { savedConfigs.value.splice(i, 1); selectOrCreateConfig( savedConfigs.value.length > 0 ? savedConfigs.value[0].name : 'default', ); } } function selectOrCreateConfig(name: string) { const c = savedConfigs.value.find((c) => name === c.name); if (c) { config.value = structuredClone(toRaw(c)); } else { newConfig(name); } } function makeConfig({ name = '', pairlists = [], blacklist = [] } = {}): PairlistConfig { return { name, pairlists, blacklist }; } function addToBlacklist() { config.value.blacklist.push(''); } function removeFromBlacklist(index: number) { config.value.blacklist.splice(index, 1); } function duplicateBlacklist(configName: string) { const conf = savedConfigs.value.find((c) => c.name === configName); if (conf) { config.value.blacklist = structuredClone(toRaw(conf.blacklist)); } } async function startPairlistEvaluation() { const payload: PairlistsPayload = configToPayload(); evaluating.value = true; try { const { job_id: jobId } = await botStore.activeBot.evaluatePairlist(payload); console.log('jobId', jobId); intervalId.value = setInterval(async () => { const res = await botStore.activeBot.getBackgroundJobStatus(jobId); if (!res.running) { clearInterval(intervalId.value); const wl = await botStore.activeBot.getPairlistEvalResult(jobId); evaluating.value = false; if (wl.status === 'success') { whitelist.value = wl.result.whitelist; } else if (wl.error) { showAlert(wl.error, 'danger'); evaluating.value = false; } } }, 1000); } catch (error) { showAlert('Evaluation failed', 'danger'); evaluating.value = false; } } function convertToParamType(type: PairlistParamType, value: PairlistParamValue) { if (type === PairlistParamType.number) { return Number(value); } else if (type === PairlistParamType.boolean) { return Boolean(value); } else { return String(value); } } function configToPayload(): PairlistsPayload { const pairlists: PairlistPayloadItem[] = configToPayloadItems(); const c: PairlistsPayload = { pairlists: pairlists, stake_currency: stakeCurrency.value, blacklist: config.value.blacklist, }; if (customExchange.value) { console.log('setting custom exchange props'); c.exchange = selectedExchange.value.exchange; c.trading_mode = selectedExchange.value.trade_mode.trading_mode; c.margin_mode = selectedExchange.value.trade_mode.margin_mode; } return c; } function configToPayloadItems() { const pairlists: PairlistPayloadItem[] = []; config.value.pairlists.forEach((config) => { const pairlist = { method: config.name, }; for (const key in config.params) { const param = config.params[key]; if (param.value) { pairlist[key] = convertToParamType(param.type, param.value); } } pairlists.push(pairlist); }); return pairlists; } watch( () => config.value, () => { configName.value = config.value.name; }, { deep: true, }, ); return { evaluating, whitelist, config, configJSON, savedConfigs, configName, startPairlistEvaluation, addToConfig, removeFromConfig, saveConfig, duplicateConfig, deleteConfig, newConfig, selectOrCreateConfig, addToBlacklist, removeFromBlacklist, duplicateBlacklist, isSavedConfig, firstPairlistIsGenerator, pairlistValid, stakeCurrency, customExchange, selectedExchange, }; }, { persist: { key: 'ftPairlistConfig', paths: ['savedConfigs', 'configName'], }, }, );