Fix pnl calculation after position closed
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user