Fix pnl calculation after position closed

This commit is contained in:
2025-07-23 03:55:47 +07:00
parent 29c960eeca
commit 598d601011

View File

@@ -1181,19 +1181,110 @@ public class TradingBot : Bot, ITradingBot
? OptimizedCandles.LastOrDefault() ? OptimizedCandles.LastOrDefault()
: ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow); : ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
if (currentCandle != null && position.ProfitAndLoss != null) if (currentCandle != null)
{ {
// Determine which trade closed the position based on realized P&L // Get the last 4 candles to check if SL/TP was actually hit
if (position.ProfitAndLoss.Realized > 0) var recentCandles = Config.IsForBacktest
? OptimizedCandles.TakeLast(4).ToList()
: await ExchangeService.GetCandlesInflux(Account.Exchange, Config.Ticker, DateTime.UtcNow.AddHours(-4), Config.Timeframe);
var minPriceRecent = recentCandles.Min(c => c.Low);
var maxPriceRecent = recentCandles.Max(c => c.High);
bool wasStopLossHit = false;
bool wasTakeProfitHit = false;
if (position.OriginDirection == TradeDirection.Long)
{
// For long positions: SL hit if recent low touched SL, TP hit if recent high touched TP
wasStopLossHit = minPriceRecent <= position.StopLoss.Price;
wasTakeProfitHit = maxPriceRecent >= position.TakeProfit1.Price;
}
else
{
// For short positions: SL hit if recent high touched SL, TP hit if recent low touched TP
wasStopLossHit = maxPriceRecent >= position.StopLoss.Price;
wasTakeProfitHit = minPriceRecent <= position.TakeProfit1.Price;
}
decimal closingPrice;
if (wasStopLossHit)
{
// Position was closed by Stop Loss execution
closingPrice = position.StopLoss.Price;
position.StopLoss.SetDate(currentCandle.Date);
Logger.LogInformation(
$"🛑 **Stop Loss Execution Confirmed**\n" +
$"Position: `{position.Identifier}`\n" +
$"SL Price: `${position.StopLoss.Price:F2}` was hit\n" +
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
}
else if (wasTakeProfitHit)
{
// Position was closed by Take Profit execution
closingPrice = position.TakeProfit1.Price;
position.TakeProfit1.SetDate(currentCandle.Date);
Logger.LogInformation(
$"🎯 **Take Profit Execution Confirmed**\n" +
$"Position: `{position.Identifier}`\n" +
$"TP Price: `${position.TakeProfit1.Price:F2}` was hit\n" +
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
}
else
{
// Manual close or exchange close - use current market price
closingPrice = Config.IsForBacktest
? currentCandle.Close
: ExchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);
// Determine if it was profitable to set the correct trade type for tracking
bool isManualCloseProfitable = position.OriginDirection == TradeDirection.Long
? closingPrice > position.Open.Price
: closingPrice < position.Open.Price;
if (isManualCloseProfitable)
{ {
// Profitable close = Take Profit
position.TakeProfit1.SetDate(currentCandle.Date); position.TakeProfit1.SetDate(currentCandle.Date);
} }
else else
{ {
// Loss or breakeven close = Stop Loss
position.StopLoss.SetDate(currentCandle.Date); position.StopLoss.SetDate(currentCandle.Date);
} }
Logger.LogInformation(
$"✋ **Manual/Exchange Close Detected**\n" +
$"Position: `{position.Identifier}`\n" +
$"SL: `${position.StopLoss.Price:F2}` | TP: `${position.TakeProfit1.Price:F2}`\n" +
$"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`\n" +
$"Closing at market price: `${closingPrice:F2}`");
}
// Calculate PnL based on actual closing price
var entryPrice = position.Open.Price;
var positionSize = position.Open.Quantity * position.Open.Leverage;
decimal pnl;
if (position.OriginDirection == TradeDirection.Long)
{
pnl = (closingPrice - entryPrice) * positionSize;
}
else
{
pnl = (entryPrice - closingPrice) * positionSize;
}
// Update position's realized PnL if not already set
if (position.ProfitAndLoss == null)
{
position.ProfitAndLoss = new ProfitAndLoss { Realized = pnl };
}
else if (position.ProfitAndLoss.Realized == 0)
{
position.ProfitAndLoss.Realized = pnl;
}
} }
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished); await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished);