frequi_origin/src/stores/ftbotwrapper.ts

339 lines
11 KiB
TypeScript
Raw Normal View History

2022-04-18 08:53:07 +00:00
import { UserService } from '@/shared/userService';
2022-04-19 04:33:25 +00:00
import {
2022-04-19 17:19:45 +00:00
BalanceInterface,
2022-04-19 04:33:25 +00:00
BotDescriptor,
BotDescriptors,
2022-04-19 17:19:45 +00:00
BotState,
2022-04-28 17:21:30 +00:00
ClosedTrade,
2022-04-19 04:33:25 +00:00
DailyPayload,
2022-04-19 17:37:04 +00:00
DailyRecord,
DailyReturnValue,
2022-04-19 04:33:25 +00:00
MultiDeletePayload,
MultiForcesellPayload,
2022-04-19 17:19:45 +00:00
ProfitInterface,
2022-04-19 04:33:25 +00:00
RenameBotPayload,
2022-04-19 17:19:45 +00:00
Trade,
2022-04-19 04:33:25 +00:00
} from '@/types';
2022-04-18 08:53:07 +00:00
import { defineStore } from 'pinia';
import { createBotSubStore } from './ftbot';
const AUTH_SELECTED_BOT = 'ftSelectedBot';
2022-04-19 17:19:45 +00:00
export type BotSubStore = ReturnType<typeof createBotSubStore>;
2022-04-18 08:53:07 +00:00
export interface SubStores {
2022-04-19 17:19:45 +00:00
[key: string]: BotSubStore;
2022-04-18 08:53:07 +00:00
}
2023-04-18 16:08:17 +00:00
export const useBotStore = defineStore('ftbot-wrapper', {
2022-04-18 08:53:07 +00:00
state: () => {
return {
selectedBot: '',
availableBots: {} as BotDescriptors,
globalAutoRefresh: true,
refreshing: false,
refreshInterval: null as number | null,
refreshIntervalSlow: null as number | null,
botStores: {} as SubStores,
};
},
getters: {
hasBots: (state) => Object.keys(state.availableBots).length > 0,
2022-04-18 11:13:45 +00:00
botCount: (state) => Object.keys(state.availableBots).length,
2022-04-18 08:53:07 +00:00
allBotStores: (state) => Object.values(state.botStores),
2022-04-19 17:19:45 +00:00
activeBot: (state) => state.botStores[state.selectedBot] as BotSubStore,
2022-04-21 05:25:32 +00:00
activeBotorUndefined: (state) => state.botStores[state.selectedBot] as BotSubStore | undefined,
canRunBacktest: (state) => state.botStores[state.selectedBot]?.canRunBacktest ?? false,
2022-04-18 11:13:45 +00:00
selectedBotObj: (state) => state.availableBots[state.selectedBot],
2022-04-19 04:53:35 +00:00
nextBotId: (state) => {
let botCount = Object.keys(state.availableBots).length;
while (`ftbot.${botCount}` in state.availableBots) {
botCount += 1;
}
return `ftbot.${botCount}`;
},
2022-04-19 17:19:45 +00:00
allProfit: (state): Record<string, ProfitInterface> => {
const result: Record<string, ProfitInterface> = {};
Object.entries(state.botStores).forEach(([k, botStore]) => {
result[k] = botStore.profit;
});
return result;
},
allOpenTradeCount: (state): Record<string, number> => {
const result: Record<string, number> = {};
Object.entries(state.botStores).forEach(([k, botStore]) => {
result[k] = botStore.openTradeCount;
});
return result;
},
allOpenTrades: (state): Record<string, Trade[]> => {
const result: Record<string, Trade[]> = {};
Object.entries(state.botStores).forEach(([k, botStore]) => {
result[k] = botStore.openTrades;
});
return result;
},
allBalance: (state): Record<string, BalanceInterface> => {
const result: Record<string, BalanceInterface> = {};
Object.entries(state.botStores).forEach(([k, botStore]) => {
result[k] = botStore.balance;
});
return result;
},
allBotState: (state): Record<string, BotState> => {
const result: Record<string, BotState> = {};
Object.entries(state.botStores).forEach(([k, botStore]) => {
result[k] = botStore.botState;
});
return result;
},
allOpenTradesSelectedBots: (state): Trade[] => {
2022-04-19 17:19:45 +00:00
const result: Trade[] = [];
Object.entries(state.botStores).forEach(([, botStore]) => {
if (botStore.isSelected) {
result.push(...botStore.openTrades);
}
2022-04-19 17:19:45 +00:00
});
return result;
},
allClosedTradesSelectedBots: (state): Trade[] => {
const result: Trade[] = [];
Object.entries(state.botStores).forEach(([, botStore]) => {
if (botStore.isSelected) {
result.push(...botStore.trades);
}
});
return result.sort((a, b) =>
// Sort by close timestamp, then by tradeid
b.close_timestamp && a.close_timestamp
? b.close_timestamp - a.close_timestamp
: b.trade_id - a.trade_id,
);
},
allTradesSelectedBots: (state): ClosedTrade[] => {
2022-04-28 17:21:30 +00:00
const result: ClosedTrade[] = [];
2022-04-19 17:37:04 +00:00
Object.entries(state.botStores).forEach(([, botStore]) => {
if (botStore.isSelected) {
result.push(...botStore.trades);
}
2022-04-19 17:37:04 +00:00
});
return result;
},
allDailyStatsSelectedBots: (state): DailyReturnValue => {
2022-04-22 18:02:50 +00:00
// Return aggregated daily stats for all bots - sorted ascending.
2022-04-19 17:37:04 +00:00
const resp: Record<string, DailyRecord> = {};
Object.entries(state.botStores).forEach(([, botStore]) => {
if (botStore.isSelected) {
botStore.dailyStats?.data?.forEach((d) => {
if (!resp[d.date]) {
resp[d.date] = { ...d };
} else {
resp[d.date].abs_profit += d.abs_profit;
resp[d.date].fiat_value += d.fiat_value;
resp[d.date].trade_count += d.trade_count;
}
});
}
2022-04-19 17:37:04 +00:00
});
const dailyReturn: DailyReturnValue = {
stake_currency: 'USDT',
fiat_display_currency: 'USD',
2022-04-22 18:02:50 +00:00
data: Object.values(resp).sort((a, b) => (a.date > b.date ? 1 : -1)),
2022-04-19 17:37:04 +00:00
};
return dailyReturn;
},
2022-04-18 08:53:07 +00:00
},
actions: {
selectBot(botId: string) {
if (botId in this.availableBots) {
localStorage.setItem(AUTH_SELECTED_BOT, botId);
this.selectedBot = botId;
} else {
console.warn(`Botid ${botId} not available, but selected.`);
}
},
addBot(bot: BotDescriptor) {
if (Object.keys(this.availableBots).includes(bot.botId)) {
// throw 'Bot already present';
// TODO: handle error!
console.log('Bot already present');
return;
}
console.log('add bot', bot);
const botStore = createBotSubStore(bot.botId, bot.botName);
botStore.botAdded();
this.botStores[bot.botId] = botStore;
this.availableBots[bot.botId] = bot;
2022-04-21 05:25:32 +00:00
this.botStores = { ...this.botStores };
this.availableBots = { ...this.availableBots };
2022-04-18 08:53:07 +00:00
},
renameBot(bot: RenameBotPayload) {
if (!Object.keys(this.availableBots).includes(bot.botId)) {
// TODO: handle error!
console.error('Bot not found');
return;
}
this.botStores[bot.botId].rename(bot.botName);
this.availableBots[bot.botId].botName = bot.botName;
},
removeBot(botId: string) {
if (Object.keys(this.availableBots).includes(botId)) {
this.botStores[botId].logout();
this.botStores[botId].$dispose();
delete this.botStores[botId];
delete this.availableBots[botId];
if (this.selectedBot === botId) {
this.selectFirstBot();
}
2022-04-21 05:25:32 +00:00
this.botStores = { ...this.botStores };
this.availableBots = { ...this.availableBots };
2022-04-18 08:53:07 +00:00
} else {
console.warn(`bot ${botId} not found! could not remove`);
}
},
selectFirstBot() {
if (this.hasBots) {
const selBotId = localStorage.getItem(AUTH_SELECTED_BOT);
const firstBot = Object.keys(this.availableBots)[0];
let selBot: string | undefined = firstBot;
if (selBotId) {
selBot = Object.keys(this.availableBots).find((x) => x === selBotId);
}
this.selectBot(this.availableBots[selBot || firstBot].botId);
}
},
setGlobalAutoRefresh(value: boolean) {
// TODO: could be removed.
this.globalAutoRefresh = value;
},
2022-04-18 17:46:53 +00:00
async allRefreshFrequent(forceUpdate = false) {
2022-12-27 13:39:35 +00:00
const updates: Promise<unknown>[] = [];
2022-04-18 08:53:07 +00:00
this.allBotStores.forEach(async (e) => {
if (e.refreshNow && e.botStatusAvailable && (this.globalAutoRefresh || forceUpdate)) {
2022-04-18 17:46:53 +00:00
updates.push(e.refreshFrequent());
2022-04-18 08:53:07 +00:00
}
});
2022-04-18 17:46:53 +00:00
await Promise.all(updates);
return Promise.resolve();
2022-04-18 08:53:07 +00:00
},
async allRefreshSlow(forceUpdate = false) {
this.allBotStores.forEach(async (e) => {
if (e.refreshNow && (this.globalAutoRefresh || forceUpdate)) {
await e.refreshSlow(forceUpdate);
}
});
},
async allRefreshFull() {
if (this.refreshing) {
return;
}
this.refreshing = true;
try {
// Ensure all bots status is correct.
await this.pingAll();
const botStoreUpdates: Promise<any>[] = [];
2022-12-27 13:39:35 +00:00
this.allBotStores.forEach((bot) => {
if (bot.isBotOnline && !bot.botStatusAvailable) {
botStoreUpdates.push(bot.getState());
2022-04-18 08:53:07 +00:00
}
});
await Promise.all(botStoreUpdates);
2022-12-27 13:39:35 +00:00
const updates: Promise<void>[] = [];
2022-04-18 08:53:07 +00:00
updates.push(this.allRefreshFrequent(false));
updates.push(this.allRefreshSlow(true));
// updates.push(this.getDaily());
// updates.push(this.getBalance());
await Promise.all(updates);
console.log('refreshing_end');
} finally {
this.refreshing = false;
}
},
startRefresh() {
console.log('Starting automatic refresh.');
this.allRefreshFull();
if (!this.refreshInterval) {
// Set interval for refresh
const refreshInterval = window.setInterval(() => {
this.allRefreshFrequent();
}, 5000);
this.refreshInterval = refreshInterval;
}
if (!this.refreshIntervalSlow) {
const refreshIntervalSlow = window.setInterval(() => {
this.allRefreshSlow(false);
}, 60000);
this.refreshIntervalSlow = refreshIntervalSlow;
}
},
stopRefresh() {
console.log('Stopping automatic refresh.');
if (this.refreshInterval) {
window.clearInterval(this.refreshInterval);
this.refreshInterval = null;
}
if (this.refreshIntervalSlow) {
window.clearInterval(this.refreshIntervalSlow);
this.refreshIntervalSlow = null;
}
},
async pingAll() {
await Promise.all(
Object.entries(this.botStores).map(async ([_, v]) => {
try {
2022-04-21 18:18:28 +00:00
await v.fetchPing();
2022-04-18 08:53:07 +00:00
} catch {
// pass
}
}),
);
},
allGetState() {
Object.entries(this.botStores).map(async ([_, v]) => {
try {
await v.getState();
} catch {
// pass
}
});
},
2022-04-22 18:02:50 +00:00
async allGetDaily(payload: DailyPayload) {
2022-12-27 13:39:35 +00:00
const updates: Promise<DailyReturnValue>[] = [];
2022-04-22 18:02:50 +00:00
2022-12-27 13:39:35 +00:00
this.allBotStores.forEach((bot) => {
if (bot.isBotOnline) {
updates.push(bot.getDaily(payload));
2022-04-22 18:02:50 +00:00
}
2022-04-18 08:53:07 +00:00
});
2022-04-22 18:02:50 +00:00
await Promise.all(updates);
2022-04-18 08:53:07 +00:00
},
2022-04-19 04:33:25 +00:00
async forceSellMulti(forcesellPayload: MultiForcesellPayload) {
2022-04-21 17:36:12 +00:00
return this.botStores[forcesellPayload.botId].forceexit(forcesellPayload);
2022-04-19 04:33:25 +00:00
},
async deleteTradeMulti(deletePayload: MultiDeletePayload) {
2022-04-21 17:36:12 +00:00
return this.botStores[deletePayload.botId].deleteTrade(deletePayload.tradeid);
2022-04-19 04:33:25 +00:00
},
2023-01-31 06:11:34 +00:00
async cancelOpenOrderMulti(deletePayload: MultiDeletePayload) {
return this.botStores[deletePayload.botId].cancelOpenOrder(deletePayload.tradeid);
},
2022-04-18 08:53:07 +00:00
},
});
2022-04-19 17:47:20 +00:00
export function initBots() {
UserService.migrateLogin();
2022-04-19 17:47:20 +00:00
const botStore = useBotStore();
// This might need to be moved to the parent (?)
Object.entries(UserService.getAvailableBots()).forEach(([, v]) => {
botStore.addBot(v);
});
botStore.selectFirstBot();
2022-04-28 04:46:22 +00:00
botStore.startRefresh();
botStore.allRefreshFull();
2022-04-19 17:47:20 +00:00
}