diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs index d5fa1926..c08628ab 100644 --- a/src/Managing.Api/Controllers/BotController.cs +++ b/src/Managing.Api/Controllers/BotController.cs @@ -629,13 +629,14 @@ public class BotController : BaseController var config = await _botService.GetBotConfig(request.Identifier); // If the account is being changed, verify the user owns the new account too - if (config.AccountName != request.Config.AccountName) - { - if (!await UserOwnsBotAccount(request.Identifier, request.Config.AccountName)) - { - return Forbid("You don't have permission to use this account"); - } - } + // TODO : Uncomment this for security + // if (config.AccountName != request.Config.AccountName) + // { + // if (!await UserOwnsBotAccount(request.Identifier, request.Config.AccountName)) + // { + // return Forbid("You don't have permission to use this account"); + // } + // } // Validate and get the money management LightMoneyManagement moneyManagement = null; @@ -717,7 +718,6 @@ public class BotController : BaseController // Map the request to the full TradingBotConfig var updatedConfig = new TradingBotConfig { - AccountName = request.Config.AccountName, MoneyManagement = moneyManagement, Ticker = request.Config.Ticker, Scenario = LightScenario.FromScenario(scenarioForUpdate), // Convert to LightScenario for Orleans diff --git a/src/Managing.Application.Tests/LightMoneyManagementTests.cs b/src/Managing.Application.Tests/LightMoneyManagementTests.cs new file mode 100644 index 00000000..3e6027cf --- /dev/null +++ b/src/Managing.Application.Tests/LightMoneyManagementTests.cs @@ -0,0 +1,227 @@ +using Managing.Domain.MoneyManagements; +using Xunit; +using static Managing.Common.Enums; + +namespace Managing.Application.Tests +{ + public class LightMoneyManagementTests + { + [Fact] + public void FormatPercentage_WithPercentageValues_ShouldFormatCorrectly() + { + // Arrange + var moneyManagement = new LightMoneyManagement + { + Name = "Test1", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 10, // 10% + TakeProfit = 20, // 20% + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.1m, moneyManagement.StopLoss); + Assert.Equal(0.2m, moneyManagement.TakeProfit); + } + + [Fact] + public void FormatPercentage_WithAlreadyFormattedDecimalValues_ShouldNotFormatAgain() + { + // Arrange + var moneyManagement = new LightMoneyManagement + { + Name = "Test2", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 0.1m, // Already 0.1 (10%) + TakeProfit = 0.2m, // Already 0.2 (20%) + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.1m, moneyManagement.StopLoss); + Assert.Equal(0.2m, moneyManagement.TakeProfit); + } + + [Fact] + public void FormatPercentage_WithMixedValues_ShouldHandleCorrectly() + { + // Arrange + var moneyManagement = new LightMoneyManagement + { + Name = "Test3", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 15, // 15% (should be formatted) + TakeProfit = 0.25m, // Already 0.25 (25%) (should NOT be formatted) + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.15m, moneyManagement.StopLoss); + Assert.Equal(0.25m, moneyManagement.TakeProfit); + } + + [Fact] + public void FormatPercentage_WithOnePercentEdgeCase_ShouldHandleCorrectly() + { + // Arrange - Test 1% as decimal (0.01) - should NOT be formatted + var moneyManagement1 = new LightMoneyManagement + { + Name = "Test4a", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 0.01m, // 1% as decimal + TakeProfit = 0.02m, // 2% as decimal + Leverage = 1 + }; + + // Act + moneyManagement1.FormatPercentage(); + + // Assert + Assert.Equal(0.01m, moneyManagement1.StopLoss); + Assert.Equal(0.02m, moneyManagement1.TakeProfit); + } + + [Fact] + public void FormatPercentage_WithOnePercentAsPercentage_ShouldFormatCorrectly() + { + // Arrange - Test 1% as percentage (1) - should be formatted to 0.01 + var moneyManagement = new LightMoneyManagement + { + Name = "Test4b", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 1m, // 1% as percentage + TakeProfit = 2m, // 2% as percentage + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.01m, moneyManagement.StopLoss); + Assert.Equal(0.02m, moneyManagement.TakeProfit); + } + + [Fact] + public void FormatPercentage_WithBoundaryCondition_ShouldHandleCorrectly() + { + // Arrange - Test boundary condition (exactly 1.0 vs 0.5) + var moneyManagement = new LightMoneyManagement + { + Name = "Test5", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 1.0m, // Exactly 1.0 (boundary) + TakeProfit = 0.5m, // 0.5% (should not be formatted) + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.01m, moneyManagement.StopLoss); + Assert.Equal(0.5m, moneyManagement.TakeProfit); + } + + [Theory] + [InlineData(0.01, 0.01)] // 1% as decimal - should not change + [InlineData(0.5, 0.5)] // 0.5% as decimal - should not change + [InlineData(0.25, 0.25)] // 0.25% as decimal - should not change + public void FormatPercentage_WithDecimalValuesLessThanOne_ShouldNotFormat(decimal input, decimal expected) + { + // Arrange + var moneyManagement = new LightMoneyManagement + { + Name = "Test6", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = input, + TakeProfit = input, + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(expected, moneyManagement.StopLoss); + Assert.Equal(expected, moneyManagement.TakeProfit); + } + + [Theory] + [InlineData(1, 0.01)] // 1% as percentage - should format to 0.01 + [InlineData(5, 0.05)] // 5% as percentage - should format to 0.05 + [InlineData(10, 0.1)] // 10% as percentage - should format to 0.1 + [InlineData(50, 0.5)] // 50% as percentage - should format to 0.5 + public void FormatPercentage_WithPercentageValuesGreaterThanOrEqualToOne_ShouldFormat(decimal input, decimal expected) + { + // Arrange + var moneyManagement = new LightMoneyManagement + { + Name = "Test7", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = input, + TakeProfit = input, + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(expected, moneyManagement.StopLoss); + Assert.Equal(expected, moneyManagement.TakeProfit); + } + + [Fact] + public void FormatPercentage_With075PercentAsDecimal_ShouldNotFormat() + { + // Arrange - Test 0.75% as decimal (0.0075) - should NOT be formatted + var moneyManagement = new LightMoneyManagement + { + Name = "Test8a", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 0.01m, // 1% as decimal + TakeProfit = 0.0075m, // 0.75% as decimal + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.01m, moneyManagement.StopLoss); + Assert.Equal(0.0075m, moneyManagement.TakeProfit); + } + + [Fact] + public void FormatPercentage_With075PercentAsPercentage_ShouldNotFormat() + { + // Arrange - Test 0.75% as percentage (0.75) - should NOT be formatted because it's < 1 + // This is a limitation of the current simple logic + var moneyManagement = new LightMoneyManagement + { + Name = "Test8b", + Timeframe = Timeframe.FifteenMinutes, + StopLoss = 1m, // 1% as percentage + TakeProfit = 0.75m, // 0.75% as percentage + Leverage = 1 + }; + + // Act + moneyManagement.FormatPercentage(); + + // Assert + Assert.Equal(0.01m, moneyManagement.StopLoss); + Assert.Equal(0.75m, moneyManagement.TakeProfit); // Not formatted because < 1 + } + } +} diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 02511ddc..6ee0b302 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -996,7 +996,7 @@ public class TradingBotBase : ITradingBot { // Add position to internal collection before any status updates Positions[position.Identifier] = position; - + if (position.Open.Status != TradeStatus.Cancelled && position.Status != PositionStatus.Rejected) { SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen); @@ -2224,10 +2224,10 @@ public class TradingBotBase : ITradingBot changes.Add($"🏷️ Name: {Config.Name} → {newConfig.Name}"); } - if (Config.AccountName != newConfig.AccountName) - { - changes.Add($"👤 Account: {Config.AccountName} → {newConfig.AccountName}"); - } + // if (Config.AccountName != newConfig.AccountName) + // { + // changes.Add($"👤 Account: {Config.AccountName} → {newConfig.AccountName}"); + // } if (Config.Ticker != newConfig.Ticker) { @@ -2257,6 +2257,8 @@ public class TradingBotBase : ITradingBot // Protect critical properties that shouldn't change for running bots var protectedIsForBacktest = Config.IsForBacktest; + newConfig.AccountName = Config.AccountName; + // Update the configuration Config = newConfig; diff --git a/src/Managing.Domain/MoneyManagements/LightMoneyManagement.cs b/src/Managing.Domain/MoneyManagements/LightMoneyManagement.cs index f2215122..d60440ed 100644 --- a/src/Managing.Domain/MoneyManagements/LightMoneyManagement.cs +++ b/src/Managing.Domain/MoneyManagements/LightMoneyManagement.cs @@ -22,7 +22,15 @@ public class LightMoneyManagement public void FormatPercentage() { - StopLoss /= 100; - TakeProfit /= 100; + // Only format if values are in percentage form (>= 1), not already in decimal form (< 1) + if (StopLoss >= 1) + { + StopLoss /= 100; + } + + if (TakeProfit >= 1) + { + TakeProfit /= 100; + } } } \ No newline at end of file