Enhance SpotBot closing price calculation and logging
- Implemented logic to calculate broker closing price from PNL when the price is zero or invalid, improving accuracy in trade reconciliation. - Added detailed logging for calculated closing prices, including entry price, PNL, and leverage, to enhance visibility into trading performance. - Updated handling of take profit and stop loss updates based on valid closing prices, ensuring more reliable position management.
This commit is contained in:
@@ -1011,52 +1011,106 @@ public class SpotBot : TradingBotBase
|
||||
if (brokerPosition.Open != null)
|
||||
{
|
||||
var brokerClosingPrice = brokerPosition.Open.Price;
|
||||
var isProfitable = position.OriginDirection == TradeDirection.Long
|
||||
? position.Open.Price < brokerClosingPrice
|
||||
: position.Open.Price > brokerClosingPrice;
|
||||
|
||||
if (isProfitable)
|
||||
|
||||
// If brokerClosingPrice is 0 or invalid, calculate it from PNL
|
||||
// This handles cases where very small prices (like PEPE) lose precision
|
||||
if (brokerClosingPrice <= 0 && brokerPosition.ProfitAndLoss != null &&
|
||||
brokerPosition.ProfitAndLoss.Realized != 0 && position.Open != null)
|
||||
{
|
||||
if (position.TakeProfit1 != null)
|
||||
// Calculate closing price from PNL formula
|
||||
// For LONG: PNL = (exitPrice - entryPrice) * quantity * leverage
|
||||
// exitPrice = (PNL / (quantity * leverage)) + entryPrice
|
||||
// For SHORT: PNL = (entryPrice - exitPrice) * quantity * leverage
|
||||
// exitPrice = entryPrice - (PNL / (quantity * leverage))
|
||||
var realizedPnl = brokerPosition.ProfitAndLoss.Realized;
|
||||
var entryPrice = position.Open.Price;
|
||||
var quantity = position.Open.Quantity;
|
||||
var leverage = position.Open.Leverage;
|
||||
|
||||
if (quantity > 0 && leverage > 0)
|
||||
{
|
||||
position.TakeProfit1.Price = brokerClosingPrice;
|
||||
position.TakeProfit1.SetDate(brokerPosition.Open.Date);
|
||||
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||
var pnlPerUnit = realizedPnl / (quantity * leverage);
|
||||
|
||||
if (position.OriginDirection == TradeDirection.Long)
|
||||
{
|
||||
brokerClosingPrice = entryPrice + pnlPerUnit;
|
||||
}
|
||||
else // Short
|
||||
{
|
||||
brokerClosingPrice = entryPrice - pnlPerUnit;
|
||||
}
|
||||
|
||||
await LogDebugAsync(
|
||||
$"⚠️ Broker closing price was 0, calculated from PNL\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Entry Price: `${entryPrice:F8}`\n" +
|
||||
$"PNL: `${realizedPnl:F8}`\n" +
|
||||
$"Quantity: `{quantity:F8}`\n" +
|
||||
$"Leverage: `{leverage}`\n" +
|
||||
$"Calculated Closing Price: `${brokerClosingPrice:F8}`");
|
||||
}
|
||||
}
|
||||
|
||||
// Only update TP/SL prices if we have a valid closing price
|
||||
if (brokerClosingPrice > 0)
|
||||
{
|
||||
var isProfitable = position.OriginDirection == TradeDirection.Long
|
||||
? position.Open.Price < brokerClosingPrice
|
||||
: position.Open.Price > brokerClosingPrice;
|
||||
|
||||
if (isProfitable)
|
||||
{
|
||||
if (position.TakeProfit1 != null)
|
||||
{
|
||||
position.TakeProfit1.Price = brokerClosingPrice;
|
||||
position.TakeProfit1.SetDate(brokerPosition.Open.Date);
|
||||
position.TakeProfit1.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
|
||||
// Cancel SL trade when TP is hit
|
||||
if (position.StopLoss != null)
|
||||
{
|
||||
position.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (position.StopLoss != null)
|
||||
{
|
||||
position.StopLoss.Price = brokerClosingPrice;
|
||||
position.StopLoss.SetDate(brokerPosition.Open.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);
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel SL trade when TP is hit
|
||||
if (position.StopLoss != null)
|
||||
{
|
||||
position.StopLoss.SetStatus(TradeStatus.Cancelled);
|
||||
}
|
||||
await LogDebugAsync(
|
||||
$"📊 Spot Position Reconciliation Complete\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Closing Price: `${brokerClosingPrice:F8}`\n" +
|
||||
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
||||
$"PnL from broker: `${position.ProfitAndLoss.Realized:F8}`");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (position.StopLoss != null)
|
||||
{
|
||||
position.StopLoss.Price = brokerClosingPrice;
|
||||
position.StopLoss.SetDate(brokerPosition.Open.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);
|
||||
}
|
||||
await LogWarningAsync(
|
||||
$"⚠️ Cannot update closing trade price - broker price is invalid\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Broker PNL: `{brokerPosition.ProfitAndLoss?.Realized:F8}`\n" +
|
||||
$"Entry Price: `${position.Open?.Price:F8}`\n" +
|
||||
$"Broker Closing Price: `{brokerPosition.Open.Price}`\n" +
|
||||
$"Will skip updating TP/SL prices to avoid zero-price fills");
|
||||
}
|
||||
|
||||
await LogDebugAsync(
|
||||
$"📊 Spot Position Reconciliation Complete\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Closing Price: `${brokerClosingPrice:F2}`\n" +
|
||||
$"Used: `{(isProfitable ? "Take Profit" : "Stop Loss")}`\n" +
|
||||
$"PnL from broker: `${position.ProfitAndLoss.Realized:F2}`");
|
||||
}
|
||||
|
||||
return true; // Successfully reconciled, skip candle-based calculation
|
||||
|
||||
Reference in New Issue
Block a user