Fix pnl calculation after position closed
This commit is contained in:
@@ -1181,18 +1181,109 @@ public class TradingBot : Bot, ITradingBot
|
||||
? OptimizedCandles.LastOrDefault()
|
||||
: 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
|
||||
if (position.ProfitAndLoss.Realized > 0)
|
||||
// Get the last 4 candles to check if SL/TP was actually hit
|
||||
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)
|
||||
{
|
||||
// Profitable close = Take Profit
|
||||
position.TakeProfit1.SetDate(currentCandle.Date);
|
||||
// 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
|
||||
{
|
||||
// Loss or breakeven close = Stop Loss
|
||||
// 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)
|
||||
{
|
||||
position.TakeProfit1.SetDate(currentCandle.Date);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user