diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 1b6fbb9a..4437c8c0 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -402,6 +402,12 @@ public class TradingBotBase : ITradingBot internalPosition.ProfitAndLoss = brokerPosition.ProfitAndLoss; internalPosition.Status = PositionStatus.Filled; + // Update Open trade status when position is found on broker + if (internalPosition.Open != null) + { + internalPosition.Open.SetStatus(TradeStatus.Filled); + } + if (internalPosition.Status.Equals(PositionStatus.New)) { await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled); @@ -493,6 +499,13 @@ public class TradingBotBase : ITradingBot $"✅ **Position Found on Broker**\nPosition is already open on broker\nUpdating position status to Filled"); UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized); + + // Update Open trade status when position is found on broker with 2 orders + if (internalPosition.Open != null) + { + internalPosition.Open.SetStatus(TradeStatus.Filled); + } + await SetPositionStatus(signal.Identifier, PositionStatus.Filled); // Notify platform summary about the executed trade @@ -568,56 +581,130 @@ public class TradingBotBase : ITradingBot { if (positionForSignal.StopLoss.Price >= lastCandle.Low) { - await LogInformation( - $"🛑 **Stop Loss Hit**\nClosing LONG position\nPrice: `${positionForSignal.StopLoss.Price}`"); - await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, - positionForSignal.StopLoss.Price, true); + // Use actual execution price (lastCandle.Low for SL hit) + var executionPrice = lastCandle.Low; + positionForSignal.StopLoss.SetPrice(executionPrice, 2); + positionForSignal.StopLoss.SetDate(lastCandle.Date); positionForSignal.StopLoss.SetStatus(TradeStatus.Filled); + + // Cancel TP trades when SL is hit + if (positionForSignal.TakeProfit1 != null) + { + positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled); + } + if (positionForSignal.TakeProfit2 != null) + { + positionForSignal.TakeProfit2.SetStatus(TradeStatus.Cancelled); + } + + await LogInformation( + $"🛑 **Stop Loss Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)"); + await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, + executionPrice, true); } else if (positionForSignal.TakeProfit1.Price <= lastCandle.High && positionForSignal.TakeProfit1.Status != TradeStatus.Filled) { - await LogInformation( - $"🎯 **Take Profit 1 Hit**\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit1.Price}`"); - await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, - positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null); + // Use actual execution price (lastCandle.High for TP hit) + var executionPrice = lastCandle.High; + positionForSignal.TakeProfit1.SetPrice(executionPrice, 2); + positionForSignal.TakeProfit1.SetDate(lastCandle.Date); positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled); + + // Cancel SL trade when TP is hit + if (positionForSignal.StopLoss != null) + { + positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled); + } + + await LogInformation( + $"🎯 **Take Profit 1 Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, + executionPrice, positionForSignal.TakeProfit2 == null); } else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High) { - await LogInformation( - $"🎯 **Take Profit 2 Hit**\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit2.Price}`"); - await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, - positionForSignal.TakeProfit2.Price, true); + // Use actual execution price (lastCandle.High for TP hit) + var executionPrice = lastCandle.High; + positionForSignal.TakeProfit2.SetPrice(executionPrice, 2); + positionForSignal.TakeProfit2.SetDate(lastCandle.Date); positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled); + + // Cancel SL trade when TP is hit + if (positionForSignal.StopLoss != null) + { + positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled); + } + + await LogInformation( + $"🎯 **Take Profit 2 Hit**\nClosing LONG position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, + executionPrice, true); } } else if (positionForSignal.OriginDirection == TradeDirection.Short) { if (positionForSignal.StopLoss.Price <= lastCandle.High) { - await LogInformation( - $"🛑 **Stop Loss Hit**\nClosing SHORT position\nPrice: `${positionForSignal.StopLoss.Price}`"); - await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, - positionForSignal.StopLoss.Price, true); + // Use actual execution price (lastCandle.High for SL hit on SHORT) + var executionPrice = lastCandle.High; + positionForSignal.StopLoss.SetPrice(executionPrice, 2); + positionForSignal.StopLoss.SetDate(lastCandle.Date); positionForSignal.StopLoss.SetStatus(TradeStatus.Filled); + + // Cancel TP trades when SL is hit + if (positionForSignal.TakeProfit1 != null) + { + positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled); + } + if (positionForSignal.TakeProfit2 != null) + { + positionForSignal.TakeProfit2.SetStatus(TradeStatus.Cancelled); + } + + await LogInformation( + $"🛑 **Stop Loss Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.StopLoss.Price:F2}`)"); + await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss, + executionPrice, true); } else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low && positionForSignal.TakeProfit1.Status != TradeStatus.Filled) { - await LogInformation( - $"🎯 **Take Profit 1 Hit**\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit1.Price}`"); - await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, - positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null); + // Use actual execution price (lastCandle.Low for TP hit on SHORT) + var executionPrice = lastCandle.Low; + positionForSignal.TakeProfit1.SetPrice(executionPrice, 2); + positionForSignal.TakeProfit1.SetDate(lastCandle.Date); positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled); + + // Cancel SL trade when TP is hit + if (positionForSignal.StopLoss != null) + { + positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled); + } + + await LogInformation( + $"🎯 **Take Profit 1 Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit1.Price:F2}`)"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1, + executionPrice, positionForSignal.TakeProfit2 == null); } else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low) { - await LogInformation( - $"🎯 **Take Profit 2 Hit**\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit2.Price}`"); - await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, - positionForSignal.TakeProfit2.Price, true); + // Use actual execution price (lastCandle.Low for TP hit on SHORT) + var executionPrice = lastCandle.Low; + positionForSignal.TakeProfit2.SetPrice(executionPrice, 2); + positionForSignal.TakeProfit2.SetDate(lastCandle.Date); positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled); + + // Cancel SL trade when TP is hit + if (positionForSignal.StopLoss != null) + { + positionForSignal.StopLoss.SetStatus(TradeStatus.Cancelled); + } + + await LogInformation( + $"🎯 **Take Profit 2 Hit**\nClosing SHORT position\nPrice: `${executionPrice:F2}` (was `${positionForSignal.TakeProfit2.Price:F2}`)"); + await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2, + executionPrice, true); } } } @@ -676,6 +763,12 @@ public class TradingBotBase : ITradingBot internalPosition.Status = PositionStatus.Filled; internalPosition.ProfitAndLoss = internalPosition.ProfitAndLoss; + // Update Open trade status when position is updated to Filled + if (internalPosition.Open != null) + { + internalPosition.Open.SetStatus(TradeStatus.Filled); + } + // Save updated position to database await tradingService.UpdatePositionAsync(internalPosition); }); @@ -1137,26 +1230,52 @@ public class TradingBotBase : ITradingBot if (wasStopLossHit) { - closingPrice = position.StopLoss.Price; + // Use actual execution price based on direction + closingPrice = position.OriginDirection == TradeDirection.Long + ? minPriceRecent // For LONG, SL hits at the low + : maxPriceRecent; // For SHORT, SL hits at the high + + position.StopLoss.SetPrice(closingPrice, 2); position.StopLoss.SetDate(currentCandle.Date); position.StopLoss.SetStatus(TradeStatus.Filled); + + // Cancel TP trades when SL is hit + if (position.TakeProfit1 != null) + { + position.TakeProfit1.SetStatus(TradeStatus.Cancelled); + } + if (position.TakeProfit2 != null) + { + position.TakeProfit2.SetStatus(TradeStatus.Cancelled); + } Logger.LogInformation( $"🛑 **Stop Loss Execution Confirmed**\n" + $"Position: `{position.Identifier}`\n" + - $"SL Price: `${position.StopLoss.Price:F2}` was hit\n" + + $"SL Price: `${closingPrice:F2}` was hit (was `${position.StopLoss.Price:F2}`)\n" + $"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`"); } else if (wasTakeProfitHit) { - closingPrice = position.TakeProfit1.Price; + // Use actual execution price based on direction + closingPrice = position.OriginDirection == TradeDirection.Long + ? maxPriceRecent // For LONG, TP hits at the high + : minPriceRecent; // For SHORT, TP hits at the low + + position.TakeProfit1.SetPrice(closingPrice, 2); position.TakeProfit1.SetDate(currentCandle.Date); position.TakeProfit1.SetStatus(TradeStatus.Filled); + + // Cancel SL trade when TP is hit + if (position.StopLoss != null) + { + position.StopLoss.SetStatus(TradeStatus.Cancelled); + } Logger.LogInformation( $"🎯 **Take Profit Execution Confirmed**\n" + $"Position: `{position.Identifier}`\n" + - $"TP Price: `${position.TakeProfit1.Price:F2}` was hit\n" + + $"TP Price: `${closingPrice:F2}` was hit (was `${position.TakeProfit1.Price:F2}`)\n" + $"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`"); } else @@ -1176,13 +1295,31 @@ public class TradingBotBase : ITradingBot if (isManualCloseProfitable) { + position.TakeProfit1.SetPrice(closingPrice, 2); position.TakeProfit1.SetDate(currentCandle.Date); position.TakeProfit1.SetStatus(TradeStatus.Filled); + + // Cancel SL trade when TP is used for manual close + if (position.StopLoss != null) + { + position.StopLoss.SetStatus(TradeStatus.Cancelled); + } } else { + position.StopLoss.SetPrice(closingPrice, 2); position.StopLoss.SetDate(currentCandle.Date); position.StopLoss.SetStatus(TradeStatus.Filled); + + // Cancel TP trades when SL is used for manual close + if (position.TakeProfit1 != null) + { + position.TakeProfit1.SetStatus(TradeStatus.Cancelled); + } + if (position.TakeProfit2 != null) + { + position.TakeProfit2.SetStatus(TradeStatus.Cancelled); + } } Logger.LogInformation(