Fix stop/restart
This commit is contained in:
@@ -20,7 +20,7 @@ namespace Managing.Application.Abstractions
|
|||||||
List<Position> Positions { get; set; }
|
List<Position> Positions { get; set; }
|
||||||
Dictionary<DateTime, decimal> WalletBalances { get; set; }
|
Dictionary<DateTime, decimal> WalletBalances { get; set; }
|
||||||
Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; }
|
Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; }
|
||||||
DateTime StartupTime { get; set; }
|
DateTime StartupTime { get; }
|
||||||
DateTime CreateDate { get; }
|
DateTime CreateDate { get; }
|
||||||
DateTime PreloadSince { get; set; }
|
DateTime PreloadSince { get; set; }
|
||||||
int PreloadedCandlesCount { get; set; }
|
int PreloadedCandlesCount { get; set; }
|
||||||
|
|||||||
@@ -116,14 +116,15 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
// Send startup message only for fresh starts (not reboots)
|
// Send startup message only for fresh starts (not reboots)
|
||||||
// Consider it a reboot if the bot was created more than 5 minutes ago
|
// Consider it a reboot if the bot was created more than 5 minutes ago
|
||||||
var timeSinceCreation = DateTime.UtcNow - CreateDate;
|
var timeSinceCreation = DateTime.UtcNow - CreateDate;
|
||||||
var isReboot = timeSinceCreation.TotalMinutes > 5;
|
var isReboot = timeSinceCreation.TotalMinutes > 3;
|
||||||
|
|
||||||
StartupTime = DateTime.UtcNow;
|
|
||||||
|
|
||||||
if (!isReboot)
|
if (!isReboot)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
StartupTime = DateTime.UtcNow;
|
||||||
|
|
||||||
var indicatorNames = Indicators.Select(i => i.Type.ToString()).ToList();
|
var indicatorNames = Indicators.Select(i => i.Type.ToString()).ToList();
|
||||||
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
|
var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
|
||||||
$"📊 **Trading Setup:**\n" +
|
$"📊 **Trading Setup:**\n" +
|
||||||
@@ -157,6 +158,7 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
InitWorker(Run).GetAwaiter().GetResult();
|
InitWorker(Run).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1458,6 +1460,7 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
Identifier = backup.Identifier;
|
Identifier = backup.Identifier;
|
||||||
User = backup.User;
|
User = backup.User;
|
||||||
Status = backup.LastStatus;
|
Status = backup.LastStatus;
|
||||||
|
StartupTime = data.StartupTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -174,7 +174,18 @@ namespace Managing.Application.ManageBot
|
|||||||
bot.User = user;
|
bot.User = user;
|
||||||
// Config is already set correctly from backup data, so we only need to restore signals, positions, etc.
|
// Config is already set correctly from backup data, so we only need to restore signals, positions, etc.
|
||||||
bot.LoadBackup(backupBot);
|
bot.LoadBackup(backupBot);
|
||||||
bot.Start();
|
|
||||||
|
// Only start the bot if the backup status is Up
|
||||||
|
if (backupBot.LastStatus == BotStatus.Up)
|
||||||
|
{
|
||||||
|
// Start the bot asynchronously without waiting for completion
|
||||||
|
_ = Task.Run(() => bot.Start());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Keep the bot in Down status if it was originally Down
|
||||||
|
bot.Stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBot CreateSimpleBot(string botName, Workflow workflow)
|
public IBot CreateSimpleBot(string botName, Workflow workflow)
|
||||||
@@ -253,6 +264,9 @@ namespace Managing.Application.ManageBot
|
|||||||
// Restart the bot (this will update StartupTime)
|
// Restart the bot (this will update StartupTime)
|
||||||
bot.Restart();
|
bot.Restart();
|
||||||
|
|
||||||
|
// Start the bot asynchronously without waiting for completion
|
||||||
|
_ = Task.Run(() => bot.Start());
|
||||||
|
|
||||||
var restartMessage = $"🔄 **Bot Restarted**\n\n" +
|
var restartMessage = $"🔄 **Bot Restarted**\n\n" +
|
||||||
$"🎯 **Agent:** {bot.User.AgentName}\n" +
|
$"🎯 **Agent:** {bot.User.AgentName}\n" +
|
||||||
$"🤖 **Bot Name:** {bot.Name}\n" +
|
$"🤖 **Bot Name:** {bot.Name}\n" +
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
namespace Managing.Application.ManageBot.Commands
|
namespace Managing.Application.ManageBot.Commands
|
||||||
{
|
{
|
||||||
public class ToggleIsForWatchingCommand : IRequest<string>
|
public class RestartBotCommand : IRequest<string>
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
public BotType BotType { get; }
|
||||||
|
|
||||||
public ToggleIsForWatchingCommand(string name)
|
public RestartBotCommand(BotType botType, string name)
|
||||||
{
|
{
|
||||||
|
BotType = botType;
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using static Managing.Common.Enums;
|
|
||||||
|
|
||||||
namespace Managing.Application.ManageBot.Commands
|
namespace Managing.Application.ManageBot.Commands
|
||||||
{
|
{
|
||||||
public class RestartBotCommand : IRequest<string>
|
public class ToggleIsForWatchingCommand : IRequest<string>
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public BotType BotType { get; }
|
|
||||||
|
|
||||||
public RestartBotCommand(BotType botType, string name)
|
public ToggleIsForWatchingCommand(string name)
|
||||||
{
|
{
|
||||||
BotType = botType;
|
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,6 @@ public class LoadBackupBotCommandHandler : IRequestHandler<LoadBackupBotCommand,
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (backupBot.LastStatus == BotStatus.Down)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Skipping backup bot {Identifier} as it is marked as Down.",
|
|
||||||
backupBot.Identifier);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var activeBot = _botService.GetActiveBots().FirstOrDefault(b => b.Identifier == backupBot.Identifier);
|
var activeBot = _botService.GetActiveBots().FirstOrDefault(b => b.Identifier == backupBot.Identifier);
|
||||||
|
|
||||||
if (activeBot == null)
|
if (activeBot == null)
|
||||||
@@ -60,11 +53,21 @@ public class LoadBackupBotCommandHandler : IRequestHandler<LoadBackupBotCommand,
|
|||||||
activeBot = _botService.GetActiveBots()
|
activeBot = _botService.GetActiveBots()
|
||||||
.FirstOrDefault(b => b.Identifier == backupBot.Identifier);
|
.FirstOrDefault(b => b.Identifier == backupBot.Identifier);
|
||||||
if (activeBot != null)
|
if (activeBot != null)
|
||||||
|
{
|
||||||
|
// Check if the bot was originally Down
|
||||||
|
if (backupBot.LastStatus == BotStatus.Down)
|
||||||
|
{
|
||||||
|
result[activeBot.Identifier] = BotStatus.Down;
|
||||||
|
_logger.LogInformation("Backup bot {Identifier} loaded but kept in Down status as it was originally Down.",
|
||||||
|
backupBot.Identifier);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
result[activeBot.Identifier] = BotStatus.Up;
|
result[activeBot.Identifier] = BotStatus.Up;
|
||||||
anyBackupStarted = true;
|
anyBackupStarted = true;
|
||||||
_logger.LogInformation("Backup bot {Identifier} started successfully.",
|
_logger.LogInformation("Backup bot {Identifier} started successfully.",
|
||||||
backupBot.Identifier);
|
backupBot.Identifier);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,13 +85,12 @@ namespace Managing.Domain.Bots
|
|||||||
{
|
{
|
||||||
Status = BotStatus.Down;
|
Status = BotStatus.Down;
|
||||||
SaveBackup();
|
SaveBackup();
|
||||||
CancellationToken.Cancel();
|
// CancellationToken.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Restart()
|
public void Restart()
|
||||||
{
|
{
|
||||||
Status = BotStatus.Up;
|
Status = BotStatus.Up;
|
||||||
SaveBackup();
|
|
||||||
StartupTime = DateTime.UtcNow; // Update the startup time when the bot is restarted
|
StartupTime = DateTime.UtcNow; // Update the startup time when the bot is restarted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,17 @@ import {CardPosition, CardSignal, CardText, Toast,} from '../../components/molle
|
|||||||
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
||||||
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
|
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
|
||||||
import {TradeChart, UnifiedTradingModal} from '../../components/organism'
|
import {TradeChart, UnifiedTradingModal} from '../../components/organism'
|
||||||
import {BotClient, BotType, MoneyManagement, Position, TradingBotResponse} from '../../generated/ManagingApi'
|
import {
|
||||||
|
BotClient,
|
||||||
|
BotType,
|
||||||
|
MoneyManagement,
|
||||||
|
Position,
|
||||||
|
TradingBotResponse,
|
||||||
|
UserClient
|
||||||
|
} from '../../generated/ManagingApi'
|
||||||
import type {IBotList} from '../../global/type.tsx'
|
import type {IBotList} from '../../global/type.tsx'
|
||||||
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
||||||
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
function baseBadgeClass(isOutlined = false) {
|
function baseBadgeClass(isOutlined = false) {
|
||||||
let classes = 'text-xs badge badge-sm transition-all duration-200 hover:scale-105 '
|
let classes = 'text-xs badge badge-sm transition-all duration-200 hover:scale-105 '
|
||||||
@@ -29,6 +37,13 @@ function cardClasses(botStatus: string) {
|
|||||||
const BotList: React.FC<IBotList> = ({ list }) => {
|
const BotList: React.FC<IBotList> = ({ list }) => {
|
||||||
const { apiUrl } = useApiUrlStore()
|
const { apiUrl } = useApiUrlStore()
|
||||||
const client = new BotClient({}, apiUrl)
|
const client = new BotClient({}, apiUrl)
|
||||||
|
const userClient = new UserClient({}, apiUrl)
|
||||||
|
|
||||||
|
const { data: currentUser } = useQuery({
|
||||||
|
queryFn: () => userClient.user_GetCurrentUser(),
|
||||||
|
queryKey: ['currentUser'],
|
||||||
|
})
|
||||||
|
|
||||||
const [showMoneyManagementModal, setShowMoneyManagementModal] =
|
const [showMoneyManagementModal, setShowMoneyManagementModal] =
|
||||||
useState(false)
|
useState(false)
|
||||||
const [selectedMoneyManagement, setSelectedMoneyManagement] =
|
const [selectedMoneyManagement, setSelectedMoneyManagement] =
|
||||||
@@ -38,12 +53,16 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
const [showTradesModal, setShowTradesModal] = useState(false)
|
const [showTradesModal, setShowTradesModal] = useState(false)
|
||||||
const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ identifier: string; agentName: string } | null>(null)
|
const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ identifier: string; agentName: string } | null>(null)
|
||||||
const [showBotConfigModal, setShowBotConfigModal] = useState(false)
|
const [showBotConfigModal, setShowBotConfigModal] = useState(false)
|
||||||
const [botConfigModalMode, setBotConfigModalMode] = useState<'createBot' | 'updateBot'>('createBot')
|
|
||||||
const [selectedBotForUpdate, setSelectedBotForUpdate] = useState<{
|
const [selectedBotForUpdate, setSelectedBotForUpdate] = useState<{
|
||||||
identifier: string
|
identifier: string
|
||||||
config: any
|
config: any
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
|
// Helper function to check if current user owns the bot
|
||||||
|
const isBotOwner = (botAgentName: string): boolean => {
|
||||||
|
return currentUser?.agentName === botAgentName
|
||||||
|
}
|
||||||
|
|
||||||
function getIsForWatchingBadge(isForWatchingOnly: boolean, identifier: string) {
|
function getIsForWatchingBadge(isForWatchingOnly: boolean, identifier: string) {
|
||||||
const classes =
|
const classes =
|
||||||
baseBadgeClass() + (isForWatchingOnly ? ' bg-accent' : ' bg-primary')
|
baseBadgeClass() + (isForWatchingOnly ? ' bg-accent' : ' bg-primary')
|
||||||
@@ -204,26 +223,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCreateBotBadge() {
|
|
||||||
const classes = baseBadgeClass() + ' bg-success'
|
|
||||||
return (
|
|
||||||
<button className={classes} onClick={() => openCreateBotModal()}>
|
|
||||||
<p className="text-primary-content flex">
|
|
||||||
<PlusCircleIcon width={15}></PlusCircleIcon>
|
|
||||||
Create Bot
|
|
||||||
</p>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openCreateBotModal() {
|
|
||||||
setBotConfigModalMode('createBot')
|
|
||||||
setSelectedBotForUpdate(null)
|
|
||||||
setShowBotConfigModal(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openUpdateBotModal(bot: TradingBotResponse) {
|
function openUpdateBotModal(bot: TradingBotResponse) {
|
||||||
setBotConfigModalMode('updateBot')
|
|
||||||
setSelectedBotForUpdate({
|
setSelectedBotForUpdate({
|
||||||
identifier: bot.identifier,
|
identifier: bot.identifier,
|
||||||
config: bot.config
|
config: bot.config
|
||||||
@@ -233,12 +233,6 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap m-4 -mx-4">
|
<div className="flex flex-wrap m-4 -mx-4">
|
||||||
<div className="w-full p-2 mb-4">
|
|
||||||
<div className="flex justify-end">
|
|
||||||
{getCreateBotBadge()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{list.map((bot: TradingBotResponse, index: number) => (
|
{list.map((bot: TradingBotResponse, index: number) => (
|
||||||
<div
|
<div
|
||||||
key={index.toString()}
|
key={index.toString()}
|
||||||
@@ -246,13 +240,13 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
>
|
>
|
||||||
<div className={cardClasses(bot.status)}>
|
<div className={cardClasses(bot.status)}>
|
||||||
<figure className="w-full">
|
<figure className="w-full">
|
||||||
{
|
{bot.candles && bot.candles.length > 0 ? (
|
||||||
<TradeChart
|
<TradeChart
|
||||||
candles={bot.candles}
|
candles={bot.candles}
|
||||||
positions={bot.positions}
|
positions={bot.positions}
|
||||||
signals={bot.signals}
|
signals={bot.signals}
|
||||||
></TradeChart>
|
></TradeChart>
|
||||||
}
|
) : null}
|
||||||
</figure>
|
</figure>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
@@ -266,16 +260,18 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
{/* Info Badges */}
|
{/* Info Badges */}
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{getMoneyManagementBadge(bot.config.moneyManagement)}
|
{getMoneyManagementBadge(bot.config.moneyManagement)}
|
||||||
{getIsForWatchingBadge(bot.config.isForWatchingOnly, bot.identifier)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Badges */}
|
{/* Action Badges - Only show for bot owners */}
|
||||||
|
{isBotOwner(bot.agentName) && (
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{getIsForWatchingBadge(bot.config.isForWatchingOnly, bot.identifier)}
|
||||||
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.flipPosition ? BotType.FlippingBot : BotType.SimpleBot)}
|
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.flipPosition ? BotType.FlippingBot : BotType.SimpleBot)}
|
||||||
{getUpdateBotBadge(bot)}
|
{getUpdateBotBadge(bot)}
|
||||||
{getManualPositionBadge(bot.identifier)}
|
{getManualPositionBadge(bot.identifier)}
|
||||||
{getDeleteBadge(bot.identifier)}
|
{getDeleteBadge(bot.identifier)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -356,7 +352,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<UnifiedTradingModal
|
<UnifiedTradingModal
|
||||||
mode={botConfigModalMode}
|
mode="updateBot"
|
||||||
showModal={showBotConfigModal}
|
showModal={showBotConfigModal}
|
||||||
closeModal={() => {
|
closeModal={() => {
|
||||||
setShowBotConfigModal(false)
|
setShowBotConfigModal(false)
|
||||||
|
|||||||
@@ -1,70 +1,58 @@
|
|||||||
import { PlayIcon, StopIcon, ViewGridAddIcon } from '@heroicons/react/solid'
|
import {PlayIcon, StopIcon, ViewGridAddIcon} from '@heroicons/react/solid'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, {useState} from 'react'
|
||||||
import type { SubmitHandler } from 'react-hook-form'
|
|
||||||
import { useForm } from 'react-hook-form'
|
|
||||||
import 'react-toastify/dist/ReactToastify.css'
|
import 'react-toastify/dist/ReactToastify.css'
|
||||||
|
|
||||||
import { Hub } from '../../app/providers/Hubs'
|
|
||||||
import useApiUrlStore from '../../app/store/apiStore'
|
import useApiUrlStore from '../../app/store/apiStore'
|
||||||
import { Toast } from '../../components/mollecules'
|
import {Toast} from '../../components/mollecules'
|
||||||
import type {
|
import {UnifiedTradingModal} from '../../components/organism'
|
||||||
Account,
|
import type {TradingBotResponse,} from '../../generated/ManagingApi'
|
||||||
Scenario,
|
import {BotClient, UserClient,} from '../../generated/ManagingApi'
|
||||||
StartBotRequest,
|
|
||||||
TradingBot,
|
|
||||||
} from '../../generated/ManagingApi'
|
|
||||||
import {
|
|
||||||
AccountClient,
|
|
||||||
ScenarioClient,
|
|
||||||
BotClient,
|
|
||||||
BotType,
|
|
||||||
Ticker,
|
|
||||||
Timeframe,
|
|
||||||
} from '../../generated/ManagingApi'
|
|
||||||
|
|
||||||
import BotList from './botList'
|
import BotList from './botList'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
const Bots: React.FC = () => {
|
const Bots: React.FC = () => {
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [activeTab, setActiveTab] = useState(0)
|
||||||
const { register, handleSubmit } = useForm<StartBotRequest>()
|
const [showBotConfigModal, setShowBotConfigModal] = useState(false)
|
||||||
const { apiUrl } = useApiUrlStore()
|
const { apiUrl } = useApiUrlStore()
|
||||||
const botClient = new BotClient({}, apiUrl)
|
const botClient = new BotClient({}, apiUrl)
|
||||||
|
const userClient = new UserClient({}, apiUrl)
|
||||||
const onSubmit: SubmitHandler<StartBotRequest> = async (form) => {
|
|
||||||
closeModal()
|
|
||||||
const t = new Toast('Bot is starting')
|
|
||||||
await botClient
|
|
||||||
.bot_Start(form)
|
|
||||||
.then((status: string) => {
|
|
||||||
t.update('success', 'Bot status : ' + status)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
t.update('error', err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data: bots } = useQuery({
|
const { data: bots } = useQuery({
|
||||||
queryFn: () => botClient.bot_GetActiveBots(),
|
queryFn: () => botClient.bot_GetActiveBots(),
|
||||||
queryKey: ['bots'],
|
queryKey: ['bots'],
|
||||||
})
|
})
|
||||||
|
|
||||||
const scenarioClient = new ScenarioClient({}, apiUrl)
|
const { data: currentUser } = useQuery({
|
||||||
const { data: scenarios } = useQuery({
|
queryFn: () => userClient.user_GetCurrentUser(),
|
||||||
queryFn: () => scenarioClient.scenario_GetScenarios(),
|
queryKey: ['currentUser'],
|
||||||
queryKey: ['scenarios'],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const accountClient = new AccountClient({}, apiUrl)
|
// Filter bots based on active tab and current user
|
||||||
const { data: accounts } = useQuery({
|
const getFilteredBots = (): TradingBotResponse[] => {
|
||||||
queryFn: () => accountClient.account_GetAccounts(),
|
if (!bots || !currentUser) return []
|
||||||
queryKey: ['accounts'],
|
|
||||||
})
|
switch (activeTab) {
|
||||||
|
case 0: // All Active Bots
|
||||||
|
return bots.filter(bot => bot.status === 'Up')
|
||||||
|
case 1: // My Active Bots
|
||||||
|
return bots.filter(bot => bot.status === 'Up' && bot.agentName === currentUser.agentName)
|
||||||
|
case 2: // My Down Bots
|
||||||
|
return bots.filter(bot => bot.status === 'Down' && bot.agentName === currentUser.agentName)
|
||||||
|
default:
|
||||||
|
return bots
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredBots = getFilteredBots()
|
||||||
|
|
||||||
|
function openCreateBotModal() {
|
||||||
|
setShowBotConfigModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
// const setupHubConnection = async () => {
|
// const setupHubConnection = async () => {
|
||||||
// const hub = new Hub('bothub', apiUrl).hub
|
// const hub = new Hub('bothub', apiUrl).hub
|
||||||
|
|
||||||
// hub.on('BotsSubscription', (bots: TradingBot[]) => {
|
// hub.on('BotsSubscription', (bots: TradingBotResponse[]) => {
|
||||||
// // eslint-disable-next-line no-console
|
// // eslint-disable-next-line no-console
|
||||||
// console.log(
|
// console.log(
|
||||||
// 'bot List',
|
// 'bot List',
|
||||||
@@ -78,14 +66,6 @@ const Bots: React.FC = () => {
|
|||||||
// return hub
|
// return hub
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function openModal() {
|
|
||||||
setShowModal(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
setShowModal(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stopAllBots() {
|
async function stopAllBots() {
|
||||||
const t = new Toast('Stoping all bots')
|
const t = new Toast('Stoping all bots')
|
||||||
await botClient
|
await botClient
|
||||||
@@ -110,11 +90,19 @@ const Bots: React.FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ label: 'All Active Bots', index: 0 },
|
||||||
|
{ label: 'My Active Bots', index: 1 },
|
||||||
|
{ label: 'My Down Bots', index: 2 },
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="container mx-auto">
|
<div className="container mx-auto">
|
||||||
<div className="tooltip" data-tip="Run new bot">
|
{/* Action Buttons */}
|
||||||
<button className="btn btn-primary m-1 text-xs" onClick={openModal}>
|
<div className="flex gap-2 mb-4">
|
||||||
|
<div className="tooltip" data-tip="Create new bot">
|
||||||
|
<button className="btn btn-primary m-1 text-xs" onClick={openCreateBotModal}>
|
||||||
<ViewGridAddIcon width="20"></ViewGridAddIcon>
|
<ViewGridAddIcon width="20"></ViewGridAddIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,142 +119,32 @@ const Bots: React.FC = () => {
|
|||||||
<PlayIcon width="20"></PlayIcon>
|
<PlayIcon width="20"></PlayIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<BotList list={bots || []} />
|
</div>
|
||||||
{showModal ? (
|
|
||||||
<>
|
{/* Tabs */}
|
||||||
<div>
|
<div className="tabs tabs-boxed mb-4">
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
{tabs.map((tab) => (
|
||||||
<div className="modal modal-bottom sm:modal-middle modal-open">
|
|
||||||
<div className="modal-box">
|
|
||||||
<button
|
<button
|
||||||
onClick={closeModal}
|
key={tab.index}
|
||||||
className="btn btn-sm btn-circle right-2 top-2 absolute"
|
className={`tab ${activeTab === tab.index ? 'tab-active' : ''}`}
|
||||||
|
onClick={() => setActiveTab(tab.index)}
|
||||||
>
|
>
|
||||||
✕
|
{tab.label}
|
||||||
</button>
|
</button>
|
||||||
<div className="text-primary mb-3 text-xl">Run a bot</div>
|
|
||||||
<div className="form-control">
|
|
||||||
<div className="input-group">
|
|
||||||
<label htmlFor="accountName" className="label mr-6">
|
|
||||||
Account
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="select select-bordered w-full h-auto max-w-xs"
|
|
||||||
{...register('accountName')}
|
|
||||||
>
|
|
||||||
{accounts.map((item) => (
|
|
||||||
<option
|
|
||||||
style={{ color: 'black' }}
|
|
||||||
key={item.name}
|
|
||||||
value={item.name}
|
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</option>
|
|
||||||
))}
|
))}
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="form-control">
|
{/* Bot List */}
|
||||||
<div className="input-group">
|
<BotList list={filteredBots} />
|
||||||
<label htmlFor="botName" className="label mr-6">
|
|
||||||
Name
|
{/* Unified Trading Modal */}
|
||||||
</label>
|
<UnifiedTradingModal
|
||||||
<input
|
mode="createBot"
|
||||||
className="w-full max-w-xs"
|
showModal={showBotConfigModal}
|
||||||
{...register('botName')}
|
closeModal={() => {
|
||||||
></input>
|
setShowBotConfigModal(false)
|
||||||
</div>
|
}}
|
||||||
</div>
|
/>
|
||||||
<div className="form-control">
|
|
||||||
<div className="input-group">
|
|
||||||
<label htmlFor="botType" className="label mr-6">
|
|
||||||
Type
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="select w-full max-w-xs"
|
|
||||||
{...register('botType')}
|
|
||||||
>
|
|
||||||
{Object.keys(BotType).map((item) => (
|
|
||||||
<option key={item} value={item}>
|
|
||||||
{item}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="form-control">
|
|
||||||
<div className="input-group">
|
|
||||||
<label htmlFor="ticker" className="label mr-6">
|
|
||||||
Ticker
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="select w-full max-w-xs"
|
|
||||||
{...register('ticker')}
|
|
||||||
>
|
|
||||||
{Object.keys(Ticker).map((item) => (
|
|
||||||
<option key={item} value={item}>
|
|
||||||
{item}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="form-control">
|
|
||||||
<div className="input-group">
|
|
||||||
<label htmlFor="timeframe" className="label mr-6">
|
|
||||||
Timeframes
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="select w-full max-w-xs"
|
|
||||||
{...register('timeframe')}
|
|
||||||
>
|
|
||||||
{Object.keys(Timeframe).map((item) => (
|
|
||||||
<option key={item} value={item}>
|
|
||||||
{item}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="form-control">
|
|
||||||
<div className="input-group">
|
|
||||||
<label htmlFor="scenario" className="label mr-6">
|
|
||||||
Scenario
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="select select-bordered w-full h-auto max-w-xs"
|
|
||||||
{...register('scenario')}
|
|
||||||
>
|
|
||||||
{scenarios.map((item) => (
|
|
||||||
<option key={item.name} value={item.name}>
|
|
||||||
{item.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="form-control">
|
|
||||||
<div className="input-group">
|
|
||||||
<label htmlFor="isForWatchOnly" className="label mr-6">
|
|
||||||
Watch Only
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
{...register('isForWatchOnly')}
|
|
||||||
></input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="modal-action">
|
|
||||||
<button type="submit" className="btn">
|
|
||||||
Run
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user