From 0ee190786eccb36d31b7cd4b1db507c00f393fe3 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Tue, 18 Nov 2025 13:58:50 +0700 Subject: [PATCH] Prevent user to open multiple strategy on the same ticker --- src/Managing.Api/Controllers/BotController.cs | 20 ++++++++++++++++++ .../Abstractions/IBotService.cs | 8 +++++++ .../ManageBot/BotService.cs | 8 +++++++ .../ManageBot/RestartBotCommandHandler.cs | 21 +++++++++++++++++++ .../ManageBot/StartBotCommandHandler.cs | 9 ++++++++ .../StartCopyTradingCommandHandler.cs | 9 ++++++++ 6 files changed, 75 insertions(+) diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs index d4dbfb67..0dc0ba70 100644 --- a/src/Managing.Api/Controllers/BotController.cs +++ b/src/Managing.Api/Controllers/BotController.cs @@ -139,6 +139,11 @@ public class BotController : BaseController await NotifyBotSubscriberAsync(); return Ok(result); } + catch (InvalidOperationException ex) when (ex.Message.Contains("already have a strategy")) + { + // Return 400 for validation errors about existing strategies on same ticker + return BadRequest(ex.Message); + } catch (Exception ex) { _logger.LogError(ex, "Error starting bot"); @@ -168,6 +173,11 @@ public class BotController : BaseController await NotifyBotSubscriberAsync(); return Ok(result); } + catch (InvalidOperationException ex) when (ex.Message.Contains("already have a strategy")) + { + // Return 400 for validation errors about existing strategies on same ticker + return BadRequest(ex.Message); + } catch (Exception ex) { _logger.LogError(ex, "Error starting copy trading bot"); @@ -192,6 +202,11 @@ public class BotController : BaseController return Ok(result); } + catch (InvalidOperationException ex) when (ex.Message.Contains("already have a strategy")) + { + // Return 400 for validation errors about existing strategies on same ticker + return BadRequest(ex.Message); + } catch (Exception ex) { _logger.LogError(ex, "Error saving bot"); @@ -319,6 +334,11 @@ public class BotController : BaseController return Ok(result); } + catch (InvalidOperationException ex) when (ex.Message.Contains("already have another strategy")) + { + // Return 400 for validation errors about existing strategies on same ticker + return BadRequest(ex.Message); + } catch (Exception ex) { _logger.LogError(ex, "Error restarting bot"); diff --git a/src/Managing.Application/Abstractions/IBotService.cs b/src/Managing.Application/Abstractions/IBotService.cs index 2ebb717b..7262e611 100644 --- a/src/Managing.Application/Abstractions/IBotService.cs +++ b/src/Managing.Application/Abstractions/IBotService.cs @@ -64,4 +64,12 @@ public interface IBotService /// The account to check balances for /// Balance check result Task CheckAccountBalancesAsync(Account account); + + /// + /// Checks if the user already has a bot (Running or Saved) on the specified ticker + /// + /// The user ID + /// The ticker to check + /// True if the user has a bot on this ticker, false otherwise + Task HasUserBotOnTickerAsync(int userId, Ticker ticker); } \ No newline at end of file diff --git a/src/Managing.Application/ManageBot/BotService.cs b/src/Managing.Application/ManageBot/BotService.cs index a78094df..d4bd3294 100644 --- a/src/Managing.Application/ManageBot/BotService.cs +++ b/src/Managing.Application/ManageBot/BotService.cs @@ -524,5 +524,13 @@ namespace Managing.Application.ManageBot }; } } + + public async Task HasUserBotOnTickerAsync(int userId, Ticker ticker) + { + var userBots = await _botRepository.GetBotsByUserIdAsync(userId); + return userBots.Any(bot => + bot.Ticker == ticker && + (bot.Status == BotStatus.Running || bot.Status == BotStatus.Saved)); + } } } \ No newline at end of file diff --git a/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs b/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs index baf5fe99..4aa96307 100644 --- a/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs +++ b/src/Managing.Application/ManageBot/RestartBotCommandHandler.cs @@ -16,6 +16,27 @@ namespace Managing.Application.ManageBot public async Task Handle(RestartBotCommand request, CancellationToken cancellationToken) { + // Get the bot being restarted to check its ticker + var bot = await _botService.GetBotByIdentifier(request.Identifier); + if (bot == null) + { + throw new ArgumentException($"Bot with identifier {request.Identifier} not found"); + } + + // Check if user already has another bot on this ticker (excluding the one being restarted) + var userBots = await _botService.GetBotsByUser(bot.User.Id); + var hasAnotherBotOnSameTicker = userBots.Any(b => + b.Identifier != request.Identifier && // Exclude the bot being restarted + b.Ticker == bot.Ticker && + (b.Status == BotStatus.Running || b.Status == BotStatus.Saved)); + + if (hasAnotherBotOnSameTicker) + { + throw new InvalidOperationException( + $"You already have another strategy running or saved on ticker {bot.Ticker}. " + + "You cannot restart this bot while you have multiple strategies on the same ticker."); + } + return await _botService.RestartBot(request.Identifier); } } diff --git a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs index 63484fb4..ac26d199 100644 --- a/src/Managing.Application/ManageBot/StartBotCommandHandler.cs +++ b/src/Managing.Application/ManageBot/StartBotCommandHandler.cs @@ -43,6 +43,15 @@ namespace Managing.Application.ManageBot $"Bot trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}"); } + // Check if user already has a bot on this ticker + var hasExistingBotOnTicker = await _botService.HasUserBotOnTickerAsync(request.User.Id, request.Config.Ticker); + if (hasExistingBotOnTicker) + { + throw new InvalidOperationException( + $"You already have a strategy running or saved on ticker {request.Config.Ticker}. " + + "You cannot create multiple strategies on the same ticker."); + } + Account account; if (string.IsNullOrEmpty(request.Config.AccountName)) { diff --git a/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs b/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs index ddb893bb..2f8446e3 100644 --- a/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs +++ b/src/Managing.Application/ManageBot/StartCopyTradingCommandHandler.cs @@ -72,6 +72,15 @@ namespace Managing.Application.ManageBot throw new InvalidOperationException($"Could not retrieve configuration for master bot {request.MasterBotIdentifier}"); } + // Check if user already has a bot on this ticker (same as master bot) + var hasExistingBotOnTicker = await _botService.HasUserBotOnTickerAsync(request.User.Id, masterConfig.Ticker); + if (hasExistingBotOnTicker) + { + throw new InvalidOperationException( + $"You already have a strategy running or saved on ticker {masterConfig.Ticker}. " + + "You cannot create multiple strategies on the same ticker."); + } + // Get account information from the requesting user's accounts var userAccounts = await _accountService.GetAccountsByUserAsync(request.User, true, true); var firstAccount = userAccounts.FirstOrDefault();