Add ETH and USDC balance check before start/restart bot and autoswap

This commit is contained in:
2025-09-23 14:03:46 +07:00
parent d13ac9fd21
commit 40f3c66694
23 changed files with 847 additions and 284 deletions

View File

@@ -3,7 +3,10 @@ using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Bots;
using Managing.Application.Bots.Models;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers;
@@ -103,12 +106,31 @@ namespace Managing.Application.ManageBot
}
var botGrain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
// Check balances for EVM/GMX V2 bots before starting/restarting
var botConfig = await botGrain.GetConfiguration();
var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(
_scopeFactory,
async accountService => await accountService.GetAccount(botConfig.AccountName, true, false));
if (account.Exchange == TradingExchanges.Evm || account.Exchange == TradingExchanges.GmxV2)
{
var balanceCheckResult = await CheckAccountBalancesAsync(account);
if (!balanceCheckResult.IsSuccessful)
{
_tradingBotLogger.LogWarning(
"Bot {Identifier} restart blocked due to insufficient balances: {Message}",
identifier, balanceCheckResult.Message);
throw new InvalidOperationException(balanceCheckResult.Message);
}
}
var grainState = await botGrain.GetBotDataAsync();
if (previousStatus == BotStatus.Saved)
{
// First time startup
await botGrain.StartAsync();
var grainState = await botGrain.GetBotDataAsync();
var account = await botGrain.GetAccount();
var startupMessage = $"🚀 **Bot Started**\n\n" +
$"🎯 **Agent:** {account.User.AgentName}\n" +
$"🤖 **Bot Name:** {grainState.Config.Name}\n" +
@@ -122,8 +144,6 @@ namespace Managing.Application.ManageBot
{
// Restart (bot was previously down)
await botGrain.RestartAsync();
var grainState = await botGrain.GetBotDataAsync();
var account = await botGrain.GetAccount();
var restartMessage = $"🔄 **Bot Restarted**\n\n" +
$"🎯 **Agent:** {account.User.AgentName}\n" +
$"🤖 **Bot Name:** {grainState.Config.Name}\n" +
@@ -138,8 +158,8 @@ namespace Managing.Application.ManageBot
}
catch (Exception e)
{
_tradingBotLogger.LogError(e, "Error restarting bot {Identifier}", identifier);
return BotStatus.Stopped;
SentrySdk.CaptureException(e);
throw;
}
}
@@ -296,7 +316,6 @@ namespace Managing.Application.ManageBot
var existingBot = await _botRepository.GetBotByIdentifierAsync(bot.Identifier);
// Check if bot already exists in database
await ServiceScopeHelpers.WithScopedService<IBotRepository>(
@@ -313,13 +332,13 @@ namespace Managing.Application.ManageBot
"Created new bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}",
bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees);
}
else if (existingBot.Status != bot.Status
|| existingBot.Pnl != Math.Round(bot.Pnl, 8)
|| existingBot.Roi != Math.Round(bot.Roi, 8)
|| existingBot.Volume != Math.Round(bot.Volume, 8)
|| existingBot.Fees != Math.Round(bot.Fees, 8)
|| existingBot.LongPositionCount != bot.LongPositionCount
|| existingBot.ShortPositionCount != bot.ShortPositionCount)
else if (existingBot.Status != bot.Status
|| existingBot.Pnl != Math.Round(bot.Pnl, 8)
|| existingBot.Roi != Math.Round(bot.Roi, 8)
|| existingBot.Volume != Math.Round(bot.Volume, 8)
|| existingBot.Fees != Math.Round(bot.Fees, 8)
|| existingBot.LongPositionCount != bot.LongPositionCount
|| existingBot.ShortPositionCount != bot.ShortPositionCount)
{
_tradingBotLogger.LogInformation("Update bot statistics for bot {BotId}",
bot.Identifier);
@@ -365,5 +384,79 @@ namespace Managing.Application.ManageBot
sortDirection);
});
}
/// <summary>
/// Checks USDC and ETH balances for EVM/GMX V2 accounts
/// </summary>
/// <param name="account">The account to check balances for</param>
/// <returns>Balance check result</returns>
public async Task<BalanceCheckResult> CheckAccountBalancesAsync(Account account)
{
try
{
return await ServiceScopeHelpers
.WithScopedServices<IExchangeService, IAccountService, BalanceCheckResult>(
_scopeFactory,
async (exchangeService, accountService) =>
{
// Get current balances
var balances = await exchangeService.GetBalances(account);
var ethBalance = balances.FirstOrDefault(b => b.TokenName?.ToUpper() == "ETH");
var usdcBalance = balances.FirstOrDefault(b => b.TokenName?.ToUpper() == "USDC");
var ethValueInUsd = ethBalance?.Amount * ethBalance?.Price ?? 0;
var usdcValue = usdcBalance?.Value ?? 0;
_tradingBotLogger.LogInformation(
"Balance check for bot restart - Account: {AccountName}, ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD",
account.Name, ethValueInUsd, usdcValue);
// Check USDC minimum balance
if (usdcValue < Constants.GMX.Config.MinimumPositionAmount)
{
return new BalanceCheckResult
{
IsSuccessful = false,
FailureReason = BalanceCheckFailureReason.InsufficientUsdcBelowMinimum,
Message =
$"USDC balance ({usdcValue:F2} USD) is below minimum required amount ({Constants.GMX.Config.MinimumPositionAmount} USD). Please add more USDC to restart the bot.",
ShouldStopBot = true
};
}
// Check ETH minimum balance for trading
if (ethValueInUsd < Constants.GMX.Config.MinimumTradeEthBalanceUsd)
{
return new BalanceCheckResult
{
IsSuccessful = false,
FailureReason = BalanceCheckFailureReason.InsufficientEthBelowMinimum,
Message =
$"ETH balance ({ethValueInUsd:F2} USD) is below minimum required amount ({Constants.GMX.Config.MinimumTradeEthBalanceUsd} USD) for trading. Please add more ETH to restart the bot.",
ShouldStopBot = true
};
}
return new BalanceCheckResult
{
IsSuccessful = true,
FailureReason = BalanceCheckFailureReason.None,
Message = "Balance check successful - Sufficient USDC and ETH balances",
ShouldStopBot = false
};
});
}
catch (Exception ex)
{
_tradingBotLogger.LogError(ex, "Error checking balances for account {AccountName}", account.Name);
return new BalanceCheckResult
{
IsSuccessful = false,
FailureReason = BalanceCheckFailureReason.BalanceFetchError,
Message = $"Failed to check balances: {ex.Message}",
ShouldStopBot = false
};
}
}
}
}