Add reconcilliation for cancelled position if needed
This commit is contained in:
@@ -423,11 +423,135 @@ public class SpotBot : TradingBotBase
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
protected override Task<bool> ReconcileWithBrokerHistory(Position position, Candle currentCandle)
|
||||
protected override async Task<bool> ReconcileWithBrokerHistory(Position position, Candle currentCandle)
|
||||
{
|
||||
// Spot trading doesn't have broker position history like futures
|
||||
// Return false to continue with candle-based calculation
|
||||
return Task.FromResult(false);
|
||||
// Spot-specific: reconcile with spot position history
|
||||
try
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"🔍 Fetching Spot Position History\nPosition: `{position.Identifier}`\nTicker: `{Config.Ticker}`");
|
||||
|
||||
var positionHistory = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>(
|
||||
_scopeFactory,
|
||||
async exchangeService =>
|
||||
{
|
||||
// Get position history from the last 24 hours for better coverage
|
||||
var fromDate = DateTime.UtcNow.AddHours(-24);
|
||||
var toDate = DateTime.UtcNow;
|
||||
return await exchangeService.GetSpotPositionHistory(Account, Config.Ticker, fromDate, toDate);
|
||||
});
|
||||
|
||||
// Find the most recent position in history
|
||||
if (positionHistory != null && positionHistory.Any())
|
||||
{
|
||||
// Get the most recent position from spot history (ordered by date)
|
||||
var brokerPosition = positionHistory
|
||||
.OrderByDescending(p => p.Open?.Date ?? p.Date)
|
||||
.FirstOrDefault();
|
||||
|
||||
// For spot trading, SHORT direction means the spot was sold/closed
|
||||
// We need to verify the last position is SHORT to confirm the position was correctly closed
|
||||
if (brokerPosition != null && brokerPosition.OriginDirection == TradeDirection.Short)
|
||||
{
|
||||
if (brokerPosition.ProfitAndLoss != null)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"✅ Spot Position History Found\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Last Position Direction: `{brokerPosition.OriginDirection}` (SHORT = Sold/Closed) ✅\n" +
|
||||
$"Realized PnL (after fees): `${brokerPosition.ProfitAndLoss.Realized:F2}`\n" +
|
||||
$"Bot's UI Fees: `${position.UiFees:F2}`\n" +
|
||||
$"Bot's Gas Fees: `${position.GasFees:F2}`");
|
||||
|
||||
// Use the actual spot PnL data from broker history
|
||||
// For spot, fees are simpler (no leverage, no closing UI fees)
|
||||
var totalBotFees = position.GasFees + position.UiFees;
|
||||
var brokerRealizedPnl = brokerPosition.ProfitAndLoss.Realized;
|
||||
|
||||
position.ProfitAndLoss = new ProfitAndLoss
|
||||
{
|
||||
Realized = brokerRealizedPnl,
|
||||
Net = brokerRealizedPnl - totalBotFees
|
||||
};
|
||||
|
||||
// Update the closing trade price if available
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
else if (brokerPosition != null)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"⚠️ Last Position Not SHORT\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Last Position Direction: `{brokerPosition.OriginDirection}`\n" +
|
||||
$"Expected SHORT to confirm spot was sold/closed\n" +
|
||||
$"Will continue with candle-based calculation");
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No matching position found or not reconciled, continue with candle-based calculation
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error reconciling spot position with broker history for position {PositionId}", position.Identifier);
|
||||
await LogWarningAsync(
|
||||
$"⚠️ Error During Spot Position History Reconciliation\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Error: {ex.Message}\n" +
|
||||
$"Will continue with candle-based calculation");
|
||||
return false; // On error, continue with candle-based calculation
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task<(decimal closingPrice, bool pnlCalculated)> CalculatePositionClosingFromCandles(
|
||||
|
||||
@@ -1175,7 +1175,7 @@ public abstract class TradingBotBase : ITradingBot
|
||||
{
|
||||
Candle currentCandle = await GetCurrentCandleForPositionClose(Account, Config.Ticker.ToString());
|
||||
|
||||
// Try broker history reconciliation first (futures-specific)
|
||||
// Try broker history reconciliation first
|
||||
var brokerHistoryReconciled = await ReconcileWithBrokerHistory(position, currentCandle);
|
||||
if (brokerHistoryReconciled && !forceMarketClose)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user