Enhance SpotBot position verification and logging
- Introduced a new method to verify the execution of opening swaps in history, ensuring positions are only marked as filled after confirming successful swaps. - Improved logging to provide detailed feedback on swap confirmation status, including retries for pending swaps and error handling for verification failures. - Adjusted position status update logic to enhance robustness in managing filled positions, preventing premature status changes.
This commit is contained in:
@@ -488,10 +488,29 @@ public class SpotBot : TradingBotBase
|
||||
}
|
||||
}
|
||||
|
||||
// Token balance exists and matches position - verify position is filled
|
||||
// Token balance exists - verify the opening swap actually succeeded in history before marking as Filled
|
||||
var previousPositionStatus = internalPosition.Status;
|
||||
|
||||
// Position found on broker (token balance exists), means the position is filled
|
||||
// Only check history if position is not already Filled
|
||||
if (internalPosition.Status != PositionStatus.Filled)
|
||||
{
|
||||
// Verify the opening swap actually executed successfully
|
||||
bool swapConfirmedInHistory = await VerifyOpeningSwapInHistory(internalPosition);
|
||||
|
||||
if (!swapConfirmedInHistory)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"⏳ Opening Swap Not Yet Confirmed in History\n" +
|
||||
$"Position: `{internalPosition.Identifier}`\n" +
|
||||
$"Token Balance: `{tokenBalanceAmount:F5}`\n" +
|
||||
$"Status: `{internalPosition.Status}`\n" +
|
||||
$"Waiting for swap to appear in exchange history...\n" +
|
||||
$"Will retry on next cycle");
|
||||
return; // Don't mark as Filled yet, wait for history confirmation
|
||||
}
|
||||
}
|
||||
|
||||
// Position confirmed on broker (token balance exists AND swap confirmed in history)
|
||||
// Update position status
|
||||
internalPosition.Status = PositionStatus.Filled;
|
||||
await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled);
|
||||
@@ -669,6 +688,96 @@ public class SpotBot : TradingBotBase
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> VerifyOpeningSwapInHistory(Position position)
|
||||
{
|
||||
try
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"🔍 Verifying Opening Swap in History\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Ticker: `{Config.Ticker}`\n" +
|
||||
$"Expected Quantity: `{position.Open.Quantity:F5}`");
|
||||
|
||||
// Get swap history from exchange
|
||||
var positionHistory = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>(
|
||||
_scopeFactory,
|
||||
async exchangeService =>
|
||||
{
|
||||
// Check swaps from 1 hour before position date to now
|
||||
var fromDate = position.Date.AddHours(-1);
|
||||
var toDate = DateTime.UtcNow;
|
||||
return await exchangeService.GetSpotPositionHistory(Account, Config.Ticker, fromDate, toDate);
|
||||
});
|
||||
|
||||
if (positionHistory == null || positionHistory.Count == 0)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"ℹ️ No History Found Yet\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Swap may still be pending or history not yet indexed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// For a LONG position, the opening swap should be LONG (USDC → Token)
|
||||
// Find a matching swap in the history
|
||||
var openingSwaps = positionHistory
|
||||
.Where(p => p.OriginDirection == TradeDirection.Long &&
|
||||
Math.Abs((p.Date - position.Date).TotalMinutes) < 10) // Within 10 minutes of position creation
|
||||
.OrderBy(p => Math.Abs((p.Date - position.Date).TotalSeconds))
|
||||
.ToList();
|
||||
|
||||
if (!openingSwaps.Any())
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"ℹ️ No Matching Opening Swap Found in History\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Position Date: `{position.Date}`\n" +
|
||||
$"Searched {positionHistory.Count} history entries\n" +
|
||||
$"No LONG swaps found within 10 minutes of position creation");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if any of the swaps match our position quantity (with tolerance)
|
||||
var matchingSwap = openingSwaps.FirstOrDefault(swap =>
|
||||
{
|
||||
if (position.Open.Quantity == 0) return false;
|
||||
var quantityDifference = Math.Abs(swap.Open.Quantity - position.Open.Quantity) / position.Open.Quantity;
|
||||
return quantityDifference < 0.02m; // Within 2% tolerance
|
||||
});
|
||||
|
||||
if (matchingSwap != null)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"✅ Opening Swap Confirmed in History\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Swap Date: `{matchingSwap.Date}`\n" +
|
||||
$"Swap Quantity: `{matchingSwap.Open.Quantity:F5}`\n" +
|
||||
$"Expected Quantity: `{position.Open.Quantity:F5}`\n" +
|
||||
$"Swap successfully executed");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Found swaps around the time, but none match the quantity
|
||||
await LogDebugAsync(
|
||||
$"⚠️ Found Swaps But None Match Position Quantity\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Expected Quantity: `{position.Open.Quantity:F5}`\n" +
|
||||
$"Found {openingSwaps.Count} LONG swaps around position creation time\n" +
|
||||
$"But none match the quantity (within 2% tolerance)");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error verifying opening swap in history for position {PositionId}", position.Identifier);
|
||||
await LogWarningAsync(
|
||||
$"⚠️ Error Verifying Opening Swap\n" +
|
||||
$"Position: `{position.Identifier}`\n" +
|
||||
$"Error: {ex.Message}\n" +
|
||||
$"Assuming swap not yet confirmed, will retry on next cycle");
|
||||
return false; // On error, don't assume swap succeeded
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CheckIfOpeningSwapWasCanceled(Position position)
|
||||
{
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user