Add ETH and USDC balance check before start/restart bot and autoswap
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user