Implement force closing of remaining balance in SpotBot
- Added a new method to force close any remaining balance in a trading position if it was not fully closed. - Enhanced logging to provide detailed information during the force close process, including current price checks and retry attempts. - Implemented error handling to manage potential failures during the force close operation, ensuring manual intervention is flagged when necessary.
This commit is contained in:
@@ -1046,7 +1046,10 @@ public class SpotBot : TradingBotBase
|
|||||||
$"Ticker: {Config.Ticker}\n" +
|
$"Ticker: {Config.Ticker}\n" +
|
||||||
$"Remaining Token Balance: `{tokenBalance.Amount:F5}`\n" +
|
$"Remaining Token Balance: `{tokenBalance.Amount:F5}`\n" +
|
||||||
$"Expected: `0` or less than `{maxDustAmount:F5}` (dust)\n" +
|
$"Expected: `0` or less than `{maxDustAmount:F5}` (dust)\n" +
|
||||||
$"Position may not have been fully closed on exchange");
|
$"Attempting to force close remaining balance...");
|
||||||
|
|
||||||
|
// Attempt to force close the remaining balance
|
||||||
|
await ForceCloseRemainingBalance(closedPosition, tokenBalance.Amount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1064,4 +1067,81 @@ public class SpotBot : TradingBotBase
|
|||||||
// Don't throw - this is just a verification step
|
// Don't throw - this is just a verification step
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ForceCloseRemainingBalance(Position position, decimal remainingBalance)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await LogInformationAsync(
|
||||||
|
$"🔄 Force Closing Remaining Balance\n" +
|
||||||
|
$"Position: `{position.Identifier}`\n" +
|
||||||
|
$"Ticker: {Config.Ticker}\n" +
|
||||||
|
$"Remaining Balance: `{remainingBalance:F5}`");
|
||||||
|
|
||||||
|
// Get current price for closing
|
||||||
|
var currentPrice = await ServiceScopeHelpers.WithScopedService<IExchangeService, decimal>(
|
||||||
|
_scopeFactory,
|
||||||
|
async exchangeService => await exchangeService.GetCurrentPrice(Account, Config.Ticker));
|
||||||
|
|
||||||
|
if (currentPrice <= 0)
|
||||||
|
{
|
||||||
|
await LogWarningAsync(
|
||||||
|
$"❌ Cannot Force Close\n" +
|
||||||
|
$"Current price is invalid: `{currentPrice}`\n" +
|
||||||
|
$"Will retry on next cycle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new command to close the remaining balance
|
||||||
|
var retryCommand = new CloseSpotPositionCommand(position, position.AccountId, currentPrice);
|
||||||
|
|
||||||
|
Position retryClosedPosition = await ServiceScopeHelpers
|
||||||
|
.WithScopedServices<IExchangeService, IAccountService, ITradingService, Position>(
|
||||||
|
_scopeFactory, async (exchangeService, accountService, tradingService) =>
|
||||||
|
await new CloseSpotPositionCommandHandler(exchangeService, accountService, tradingService)
|
||||||
|
.Handle(retryCommand));
|
||||||
|
|
||||||
|
if (retryClosedPosition != null &&
|
||||||
|
(retryClosedPosition.Status == PositionStatus.Finished || retryClosedPosition.Status == PositionStatus.Flipped))
|
||||||
|
{
|
||||||
|
await LogInformationAsync(
|
||||||
|
$"✅ Remaining Balance Force Closed Successfully\n" +
|
||||||
|
$"Position: `{position.Identifier}`\n" +
|
||||||
|
$"Cleared Balance: `{remainingBalance:F5}`\n" +
|
||||||
|
$"Close Price: `${currentPrice:F2}`");
|
||||||
|
|
||||||
|
// Verify one more time that balance is now cleared
|
||||||
|
await Task.Delay(2000); // Wait for swap to complete
|
||||||
|
|
||||||
|
var finalBalance = await ServiceScopeHelpers.WithScopedService<IExchangeService, Balance?>(
|
||||||
|
_scopeFactory,
|
||||||
|
async exchangeService => await exchangeService.GetBalance(Account, Config.Ticker));
|
||||||
|
|
||||||
|
if (finalBalance is { Amount: > 0.0001m })
|
||||||
|
{
|
||||||
|
await LogWarningAsync(
|
||||||
|
$"⚠️ Balance Still Remaining After Retry\n" +
|
||||||
|
$"Position: `{position.Identifier}`\n" +
|
||||||
|
$"Remaining: `{finalBalance.Amount:F5}`\n" +
|
||||||
|
$"Manual intervention may be required");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await LogInformationAsync(
|
||||||
|
$"✅ Balance Fully Cleared After Retry\n" +
|
||||||
|
$"Position: `{position.Identifier}`\n" +
|
||||||
|
$"Final Balance: `{finalBalance?.Amount ?? 0:F5}`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Error force closing remaining balance for position {PositionId}", position.Identifier);
|
||||||
|
await LogWarningAsync(
|
||||||
|
$"❌ Force Close Failed\n" +
|
||||||
|
$"Position: `{position.Identifier}`\n" +
|
||||||
|
$"Error: {ex.Message}\n" +
|
||||||
|
$"Manual intervention may be required");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user