Enhance SpotBot logging and orphaned position handling

- Updated SpotBot to log detailed information when detecting small token balances, indicating potential gas reserves or dust.
- Introduced a minimum threshold for orphaned positions, improving decision-making on whether to open new positions.
- Enhanced logging for potential zombie positions, providing clearer warnings when token balances are missing.
- Improved force close logging to clarify the status of remaining balances after attempts to clear them.
This commit is contained in:
2026-01-05 19:49:59 +07:00
parent e880dea126
commit 25a2b202a1

View File

@@ -180,13 +180,30 @@ public class SpotBot : TradingBotBase
return false; // Don't allow opening new position until resolved
}
if (tokenBalance is { Value: > 1m })
if (tokenBalance is { Amount: > 0 })
{
// We have a token balance but no internal position - attempt to recover orphaned position
// Check if this is a meaningful balance or just gas reserves / dust
// Minimum threshold: $10 USD value to be considered an orphaned position
const decimal minOrphanedBalanceValue = 10m;
if (tokenBalance.Value < minOrphanedBalanceValue)
{
await LogDebugAsync(
$" Small Token Balance Detected (Likely Gas Reserve or Dust)\n" +
$"Ticker: {Config.Ticker}\n" +
$"Token balance: `{tokenBalance.Amount:F8}`\n" +
$"USD Value: `${tokenBalance.Value:F2}`\n" +
$"Below orphaned threshold of `${minOrphanedBalanceValue:F2}`\n" +
$"Ignoring - safe to open new position");
return true; // Safe to open new position - this is just dust/gas reserve
}
// We have a significant token balance but no internal position - attempt to recover orphaned position
await LogWarningAsync(
$"⚠️ Orphaned Token Balance Detected\n" +
$"Ticker: {Config.Ticker}\n" +
$"Token balance: `{tokenBalance.Amount:F5}`\n" +
$"Token balance: `{tokenBalance.Amount:F5}` (Value: ${tokenBalance.Value:F2})\n" +
$"Above orphaned threshold of `${minOrphanedBalanceValue:F2}`\n" +
$"But no internal position tracked\n" +
$"Attempting to recover position from database...");
@@ -413,12 +430,17 @@ public class SpotBot : TradingBotBase
// Update quantity to match actual token balance
var actualTokenBalance = tokenBalance.Amount;
if (Math.Abs(internalPosition.Open.Quantity - actualTokenBalance) > 0.0001m)
var quantityTolerance = internalPosition.Open.Quantity * 0.006m; // 0.6% tolerance for slippage
var quantityDifference = Math.Abs(internalPosition.Open.Quantity - actualTokenBalance);
if (quantityDifference > quantityTolerance)
{
await LogDebugAsync(
$"🔄 Token Balance Mismatch\n" +
$"Internal Quantity: `{internalPosition.Open.Quantity:F5}`\n" +
$"Broker Balance: `{actualTokenBalance:F5}`\n" +
$"Difference: `{quantityDifference:F5}`\n" +
$"Tolerance (0.6%): `{quantityTolerance:F5}`\n" +
$"Updating to match broker balance");
internalPosition.Open.Quantity = actualTokenBalance;
positionForSignal.Open.Quantity = actualTokenBalance;
@@ -478,10 +500,15 @@ public class SpotBot : TradingBotBase
return;
}
await LogDebugAsync(
$"⚠️ Position Status Check\n" +
$"Internal position `{internalPosition.Identifier}` shows Filled\n" +
$"But no token balance found on broker or in history\n" +
// Position is Filled but no token balance and not found in history
// This could be a zombie position - check if token balance exists via direct exchange call
await LogWarningAsync(
$"⚠️ Potential Zombie Position Detected\n" +
$"Position: `{internalPosition.Identifier}`\n" +
$"Status: Filled\n" +
$"Token balance: 0\n" +
$"Not found in exchange history\n" +
$"This position may have been closed externally or data is delayed\n" +
$"Will retry verification on next cycle");
}
}
@@ -1089,8 +1116,10 @@ public class SpotBot : TradingBotBase
{
try
{
// Prevent infinite retry loop - only attempt force close once
// The next bot cycle will handle verification
await LogInformationAsync(
$"🔄 Force Closing Remaining Balance\n" +
$"🔄 Force Closing Remaining Balance (One-Time Attempt)\n" +
$"Position: `{position.Identifier}`\n" +
$"Ticker: {Config.Ticker}\n" +
$"Remaining Balance: `{remainingBalance:F5}`");
@@ -1127,7 +1156,7 @@ public class SpotBot : TradingBotBase
$"Cleared Balance: `{remainingBalance:F5}`\n" +
$"Close Price: `${currentPrice:F2}`");
// Verify one more time that balance is now cleared
// Verify one more time that balance is now cleared (without triggering another force close)
await Task.Delay(2000); // Wait for swap to complete
var finalBalance = await ServiceScopeHelpers.WithScopedService<IExchangeService, Balance?>(
@@ -1137,15 +1166,16 @@ public class SpotBot : TradingBotBase
if (finalBalance is { Amount: > 0.0001m })
{
await LogWarningAsync(
$"⚠️ Balance Still Remaining After Retry\n" +
$"⚠️ Balance Still Remaining After Force Close Attempt\n" +
$"Position: `{position.Identifier}`\n" +
$"Remaining: `{finalBalance.Amount:F5}`\n" +
$"Manual intervention may be required");
$"This will be handled on the next bot cycle\n" +
$"Manual intervention may be required if issue persists");
}
else
{
await LogInformationAsync(
$"✅ Balance Fully Cleared After Retry\n" +
$"✅ Balance Fully Cleared After Force Close\n" +
$"Position: `{position.Identifier}`\n" +
$"Final Balance: `{finalBalance?.Amount ?? 0:F5}`");
}