From b1aa0541e28c970a7564664efa5ceaacdeae42ee Mon Sep 17 00:00:00 2001 From: cryptooda Date: Thu, 20 Nov 2025 15:38:27 +0700 Subject: [PATCH] Add test and max collateral used --- .../TradingBotCalculationsTests.cs | 158 ++++++++++++++++++ .../Shared/Helpers/TradingBox.cs | 2 +- 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/src/Managing.Domain.Tests/TradingBotCalculationsTests.cs b/src/Managing.Domain.Tests/TradingBotCalculationsTests.cs index 08b81742..1fe5718c 100644 --- a/src/Managing.Domain.Tests/TradingBotCalculationsTests.cs +++ b/src/Managing.Domain.Tests/TradingBotCalculationsTests.cs @@ -655,6 +655,51 @@ public class TradingBotCalculationsTests : TradingBoxTests #endregion + #region CalculateROI Tests + + [Fact] + public void CalculateROI_WithRealTradingBotData_ReturnsCorrectROI() + { + // Arrange - Using real trading bot data from the provided JSON + var initialBalance = 50m; + var netPnL = 4.57691m; // finalBalance - initialBalance = 54.57691 - 50 + + // Act - Using existing GetGrowthFromInitalBalance method + var result = TradingBox.GetGrowthFromInitalBalance(initialBalance, netPnL); + + // Assert + // ROI = (Final - Initial) / Initial * 100 = (54.57691 - 50) / 50 * 100 = 9.15382% + result.Should().BeApproximately(9.15382m, 0.00001m); + } + + [Theory] + [InlineData(100.0, 10.0, 10.0)] // 10% gain: initial=100, netPnL=10, expected=10% + [InlineData(100.0, -10.0, -10.0)] // 10% loss: initial=100, netPnL=-10, expected=-10% + [InlineData(100.0, 0.0, 0.0)] // No change: initial=100, netPnL=0, expected=0% + [InlineData(50.0, 4.57691, 9.15382)] // Real bot data: initial=50, netPnL=4.57691, expected=9.15382% + public void CalculateROI_WithVariousInputs_ReturnsCorrectPercentage(decimal initialBalance, decimal netPnL, decimal expectedROI) + { + // Act + var result = TradingBox.GetGrowthFromInitalBalance(initialBalance, netPnL); + + // Assert + result.Should().BeApproximately(expectedROI, 0.00001m); + } + + [Fact] + public void CalculateROI_WithZeroInitialBalance_ThrowsDivideByZeroException() + { + // Arrange + var initialBalance = 0m; + var netPnL = 100m; + + // Act & Assert + Assert.Throws(() => + TradingBox.GetGrowthFromInitalBalance(initialBalance, netPnL)); + } + + #endregion + #region Helper Methods private List CreateLossPositions(int count, TradeDirection direction = TradeDirection.Long) @@ -733,5 +778,118 @@ public class TradingBotCalculationsTests : TradingBoxTests } #endregion + + #region CalculateAgentSummaryMetrics Tests + + [Fact] + public void CalculateAgentSummaryMetrics_WithRealTradingBotPositions_ReturnsCorrectMetrics() + { + // Arrange - Create positions based on the real trading bot JSON data + var positions = new List + { + CreateRealPosition( + direction: TradeDirection.Short, + openPrice: 2.1849m, + quantity: 22.91465209m, + leverage: 5m, + takeProfitPrice: 2.1602m, + realizedPnL: 2.66162442m, + netPnL: 2.38787384m, + gasFees: 0.15m + ), + CreateRealPosition( + direction: TradeDirection.Long, + openPrice: 2.104m, + quantity: 24.69722156m, + leverage: 5m, + takeProfitPrice: 2.127m, + realizedPnL: 2.71702395m, + netPnL: 2.43569648m, + gasFees: 0.15m + ) + }; + + // Act + var metrics = TradingBox.CalculateAgentSummaryMetrics(positions); + + // Assert + // Total PnL: 2.66162442 + 2.71702395 = 5.37864837 + metrics.TotalPnL.Should().BeApproximately(5.37864837m, 0.00000001m); + + // Total Fees: 2 positions * 0.15 gas fees = 0.3 (no UI fees since positions are closed) + metrics.TotalFees.Should().Be(0.3m); + + // Net PnL: 5.37864837 - 0.3 = 5.07864837 + metrics.NetPnL.Should().BeApproximately(5.07864837m, 0.00000001m); + + // Wins: 2, Losses: 0 + metrics.Wins.Should().Be(2); + metrics.Losses.Should().Be(0); + + // Collateral: max of (openPrice * quantity) = max(2.1849 * 22.91465209, 2.104 * 24.69722156) + // = max(50.083, 51.96295416224) = 51.96295416224 + metrics.Collateral.Should().BeApproximately(51.96295416224m, 0.00000000001m); + + // ROI: (netPnL / collateral) * 100 = (5.07864837 / 51.96295416224) * 100 ≈ 9.774% + metrics.TotalROI.Should().BeApproximately(9.774m, 0.001m); + } + + #endregion + + private static Position CreateRealPosition( + TradeDirection direction, + decimal openPrice, + decimal quantity, + decimal leverage, + decimal takeProfitPrice, + decimal realizedPnL, + decimal netPnL, + decimal gasFees) + { + // Create position using the existing test helper + var position = CreateTestPosition( + openPrice: openPrice, + quantity: quantity, + direction: direction, + leverage: leverage, + positionStatus: PositionStatus.Finished, + includeTrades: true + ); + + // Override ticker to XRP + position.Ticker = Ticker.XRP; + + // Update trades to match real data + position.Open.TradeType = TradeType.Limit; + position.Open.Ticker = Ticker.XRP; + position.Open.ExchangeOrderId = Guid.NewGuid().ToString(); + + // Set TakeProfit1 to filled with the correct price + position.TakeProfit1.Status = TradeStatus.Filled; + position.TakeProfit1.Date = TestDate.AddHours(1); + position.TakeProfit1.Ticker = Ticker.XRP; + position.TakeProfit1.Price = takeProfitPrice; + position.TakeProfit1.ExchangeOrderId = Guid.NewGuid().ToString(); + position.TakeProfit1.Message = "EmptyTrade"; + + // Set StopLoss to cancelled + position.StopLoss.Status = TradeStatus.Cancelled; + position.StopLoss.Ticker = Ticker.XRP; + position.StopLoss.ExchangeOrderId = Guid.NewGuid().ToString(); + position.StopLoss.Message = "EmptyTrade"; + + // Set Profit and Loss + position.ProfitAndLoss = new ProfitAndLoss + { + Realized = realizedPnL, + Net = netPnL + }; + + // Set fees + position.UiFees = 0; + position.GasFees = gasFees; + + return position; + } } diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs index d5b09517..e65de308 100644 --- a/src/Managing.Domain/Shared/Helpers/TradingBox.cs +++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs @@ -601,7 +601,7 @@ public static class TradingBox var totalVolume = GetTotalVolumeTraded(validPositions); var wins = validPositions.Count(p => (p.ProfitAndLoss?.Net ?? 0m) > 0m); var losses = validPositions.Count(p => (p.ProfitAndLoss?.Net ?? 0m) <= 0m); - var collateral = validPositions.Sum(p => (p.Open?.Price ?? 0m) * (p.Open?.Quantity ?? 0m)); + var collateral = validPositions.Max(p => (p.Open?.Price ?? 0m) * (p.Open?.Quantity ?? 0m)); var totalROI = collateral > 0m ? (netPnL / collateral) * 100m : 0m; return new AgentSummaryMetrics(totalPnL, netPnL, totalROI, totalVolume, wins, losses, totalFees,