Enhance SpotBot slippage handling and logging
- Increased slippage tolerance from 0.6% to 0.7% to account for gas reserves. - Improved logging to provide detailed information when adjusting position quantities due to slippage or when retaining original quantities. - Updated CloseSpotPositionCommandHandler to use the position's opened quantity instead of the entire wallet balance, ensuring gas fees are preserved. - Adjusted Web3ProxyService settings for retry attempts and operation timeouts to improve performance. - Enhanced swap token implementation to handle native tokens correctly and increased operation timeout for better reliability.
This commit is contained in:
@@ -287,7 +287,7 @@ public class SpotBot : TradingBotBase
|
||||
return false;
|
||||
}
|
||||
|
||||
var tolerance = positionQuantity * 0.006m; // 0.6% tolerance for slippage
|
||||
var tolerance = positionQuantity * 0.007m; // 0.7% tolerance for slippage and gas reserve
|
||||
var difference = Math.Abs(tokenBalance - positionQuantity);
|
||||
|
||||
if (difference > tolerance)
|
||||
@@ -307,8 +307,25 @@ public class SpotBot : TradingBotBase
|
||||
lastPosition.Status = PositionStatus.Filled;
|
||||
lastPosition.Open.SetStatus(TradeStatus.Filled);
|
||||
|
||||
// Update quantity to match actual token balance
|
||||
lastPosition.Open.Quantity = tokenBalance;
|
||||
// Only update quantity if actual balance is less than position quantity (slippage loss)
|
||||
// Don't update if balance is higher (likely includes gas reserve for ETH)
|
||||
if (tokenBalance < positionQuantity)
|
||||
{
|
||||
await LogInformationAsync(
|
||||
$"📉 Adjusting Position Quantity Due to Slippage\n" +
|
||||
$"Original Quantity: `{positionQuantity:F5}`\n" +
|
||||
$"Actual Balance: `{tokenBalance:F5}`\n" +
|
||||
$"Difference: `{difference:F5}`");
|
||||
lastPosition.Open.Quantity = tokenBalance;
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogInformationAsync(
|
||||
$"ℹ️ Keeping Original Position Quantity\n" +
|
||||
$"Position Quantity: `{positionQuantity:F5}`\n" +
|
||||
$"Actual Balance: `{tokenBalance:F5}`\n" +
|
||||
$"Not updating (balance includes gas reserve or is within tolerance)");
|
||||
}
|
||||
|
||||
// Calculate current PnL
|
||||
var currentPrice = await ServiceScopeHelpers.WithScopedService<IExchangeService, decimal>(
|
||||
@@ -402,7 +419,7 @@ public class SpotBot : TradingBotBase
|
||||
// If balance is greater, it could be orphaned tokens from previous positions
|
||||
if (tokenBalanceAmount < positionQuantity)
|
||||
{
|
||||
var tolerance = positionQuantity * 0.006m; // 0.6% tolerance to account for slippage
|
||||
var tolerance = positionQuantity * 0.007m; // 0.7% tolerance to account for slippage and gas reserve
|
||||
var difference = positionQuantity - tokenBalanceAmount;
|
||||
|
||||
if (difference > tolerance)
|
||||
@@ -413,7 +430,7 @@ public class SpotBot : TradingBotBase
|
||||
$"Position Quantity: `{positionQuantity:F5}`\n" +
|
||||
$"Token Balance: `{tokenBalanceAmount:F5}`\n" +
|
||||
$"Difference: `{difference:F5}`\n" +
|
||||
$"Tolerance (0.6%): `{tolerance:F5}`\n" +
|
||||
$"Tolerance (0.7%): `{tolerance:F5}`\n" +
|
||||
$"Token balance is significantly lower than expected\n" +
|
||||
$"Skipping position synchronization");
|
||||
return; // Skip processing if balance is too low
|
||||
@@ -446,23 +463,35 @@ public class SpotBot : TradingBotBase
|
||||
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||
positionForSignal.Open.SetStatus(TradeStatus.Filled);
|
||||
|
||||
// Update quantity to match actual token balance
|
||||
// Update quantity to match actual token balance only if difference is within acceptable tolerance
|
||||
// For ETH, we need to be careful not to include gas reserve in the position quantity
|
||||
var actualTokenBalance = tokenBalance.Amount;
|
||||
var quantityTolerance = internalPosition.Open.Quantity * 0.006m; // 0.6% tolerance for slippage
|
||||
var quantityTolerance = internalPosition.Open.Quantity * 0.007m; // 0.7% tolerance for slippage and gas reserve
|
||||
var quantityDifference = Math.Abs(internalPosition.Open.Quantity - actualTokenBalance);
|
||||
|
||||
if (quantityDifference > quantityTolerance)
|
||||
// Only update if the actual balance is LESS than expected (slippage loss)
|
||||
// Don't update if balance is higher (likely includes gas reserve or orphaned tokens)
|
||||
if (actualTokenBalance < internalPosition.Open.Quantity && quantityDifference > quantityTolerance)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"🔄 Token Balance Mismatch\n" +
|
||||
$"🔄 Token Balance Lower Than Expected (Slippage)\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");
|
||||
$"Tolerance (0.7%): `{quantityTolerance:F5}`\n" +
|
||||
$"Updating to match actual balance");
|
||||
internalPosition.Open.Quantity = actualTokenBalance;
|
||||
positionForSignal.Open.Quantity = actualTokenBalance;
|
||||
}
|
||||
else if (actualTokenBalance > internalPosition.Open.Quantity)
|
||||
{
|
||||
await LogDebugAsync(
|
||||
$"ℹ️ Token Balance Higher Than Position Quantity\n" +
|
||||
$"Internal Quantity: `{internalPosition.Open.Quantity:F5}`\n" +
|
||||
$"Broker Balance: `{actualTokenBalance:F5}`\n" +
|
||||
$"Difference: `{quantityDifference:F5}`\n" +
|
||||
$"Keeping original position quantity (extra balance likely gas reserve or orphaned tokens)");
|
||||
}
|
||||
|
||||
// Calculate and update PnL based on current price
|
||||
var currentPrice = await ServiceScopeHelpers.WithScopedService<IExchangeService, decimal>(
|
||||
|
||||
@@ -48,18 +48,13 @@ public class CloseSpotPositionCommandHandler(
|
||||
{
|
||||
// For live trading, call SwapGmxTokensAsync
|
||||
var account = await accountService.GetAccountById(request.AccountId);
|
||||
var tokenBalance = await exchangeService.GetBalance(account, request.Position.Ticker);
|
||||
|
||||
if (tokenBalance == null || tokenBalance.Amount <= 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"No available balance to close spot position for {request.Position.Ticker}");
|
||||
}
|
||||
|
||||
amountToSwap = tokenBalance.Amount;
|
||||
|
||||
// Use the position's opened quantity instead of the entire wallet balance
|
||||
// This naturally leaves extra ETH for gas fees and avoids the need for explicit reservation
|
||||
amountToSwap = request.Position.Open.Quantity;
|
||||
|
||||
logger?.LogInformation(
|
||||
"Closing spot position: PositionId={PositionId}, Ticker={Ticker}, TokenBalance={TokenBalance}, Swapping to USDC",
|
||||
"Closing spot position: PositionId={PositionId}, Ticker={Ticker}, PositionQuantity={PositionQuantity}, Swapping to USDC",
|
||||
request.Position.Identifier, request.Position.Ticker, amountToSwap);
|
||||
|
||||
swapResult = await tradingService.SwapGmxTokensAsync(
|
||||
|
||||
Reference in New Issue
Block a user