using Managing.Domain.Trades; using Managing.Domain.Users; using Xunit; using static Managing.Common.Enums; namespace Managing.Application.Tests { public class ProfitAndLossTests { [Theory] [InlineData(1, 100, 110, 10)] [InlineData(2, 100, 110, 20)] public void Should_Return_Correct_ProfitAndLoss_Amount(decimal quantity, decimal price, decimal exitPrice, decimal expectedResult) { // Arrange var init = new List>(); init.Add(new Tuple(quantity, price)); var pnl = new ProfitAndLoss(init, TradeDirection.Long); // Act var result = pnl.FloatingForTheoriticalExit(exitPrice); // Assert Assert.Equal(expectedResult, result); } [Fact] public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit() { // Arrange var position = GetFakeShortPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Trigger Stop Loss position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection); // Assert Assert.Equal(20, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Long_Solana_failed_Position_After_Took_One_Profit() { // Arrange var position = GetSolanaLongPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Trigger Stop Loss position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection); // Assert Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Long_Solana_failed_Position_After_Took_One_Profit2() { // Arrange var position = GetSolanaLongPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price), new Tuple(-position.TakeProfit2.Quantity, position.StopLoss.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Assert Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit2() { // Arrange var position = GetFakeShortPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price), new Tuple(-position.TakeProfit2.Quantity, position.StopLoss.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Assert Assert.Equal(20, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit3() { // Arrange var position = GetFakeShortPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss.AddFill(-position.TakeProfit1.Quantity, position.TakeProfit1.Price, TradeDirection.Short); position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Short); // Assert Assert.Equal(20, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Short_Succeeded_Position_After_Two_Take_Profit() { // Arrange var position = GetFakeShortPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Trigger Stop Loss position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Short); // Assert Assert.Equal(120, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Long_failed_Position_After_Took_One_Profit() { // Arrange var position = GetFakeLongPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Trigger Stop Loss position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Long); // Assert Assert.Equal(20, position.ProfitAndLoss.Realized); } [Fact] public void Should_Return_Correct_Pnl_For_Long_Succeeded_Position_After_Two_Take_Profit() { // Arrange var position = GetFakeLongPosition(); // Setup Open and first take profit var orders = new List> { new Tuple(position.Open.Quantity, position.Open.Price), new Tuple(-position.TakeProfit1.Quantity, position.TakeProfit1.Price) }; // Act position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); // Trigger Stop Loss position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Long); // Assert Assert.Equal(120, position.ProfitAndLoss.Realized); } private static Position GetFakeShortPosition() { return new Position("", "FakeAccount", TradeDirection.Short, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow, new User()) { Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled, TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""), StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TradeType.StopMarket, Ticker.ADA, 10, 110, 1, "StopLossOrderId", ""), TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TradeType.TakeProfitLimit, Ticker.ADA, 6, 90, 1, "TakeProfit1OrderId", ""), TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TradeType.TakeProfitLimit, Ticker.ADA, 4, 85, 1, "TakeProfit1OrderId", "") }; } private static Position GetSolanaLongPosition() { return new Position("", "FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow, new User()) { Open = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.Filled, TradeType.Market, Ticker.ADA, (decimal)6.0800904000245037980887037491, (decimal)81.6200, 1, "OpenOrderId", ""), StopLoss = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, TradeType.StopMarket, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)79.987600, 1, "StopLossOrderId", ""), TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, TradeType.TakeProfitLimit, Ticker.ADA, (decimal)2.4320361600098015192354814996, (decimal)85.701000, 1, "TakeProfit1OrderId", ""), TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, TradeType.TakeProfitLimit, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)89.782000, 1, "TakeProfit1OrderId", "") }; } private static Position GetFakeLongPosition() { return new Position("", "FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow, new User()) { Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled, TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""), StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TradeType.StopMarket, Ticker.ADA, 10, 90, 1, "StopLossOrderId", ""), TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TradeType.TakeProfitLimit, Ticker.ADA, 6, 110, 1, "TakeProfit1OrderId", ""), TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TradeType.TakeProfitLimit, Ticker.ADA, 4, 115, 1, "TakeProfit1OrderId", "") }; } } }