Add automatic swap when low ETH

This commit is contained in:
2025-08-28 06:49:14 +07:00
parent 2e4c18ff63
commit 28fef53ff8
3 changed files with 104 additions and 14 deletions

View File

@@ -42,5 +42,8 @@ namespace Managing.Application.Abstractions
/// <param name="newConfig">The new configuration to apply</param>
/// <returns>True if the configuration was successfully updated, false otherwise</returns>
Task<bool> UpdateConfiguration(TradingBotConfig newConfig);
Task LogInformation(string message);
Task LogWarning(string message);
}
}

View File

@@ -1,5 +1,7 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
@@ -291,6 +293,70 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
return;
}
// Check broker balance before running
var balances = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Balance>>(_scopeFactory, async exchangeService =>
{
return await exchangeService.GetBalances(_tradingBot.Account, false);
});
var usdcBalance = balances.FirstOrDefault(b => b.TokenName == Ticker.USDC.ToString());
var ethBalance = balances.FirstOrDefault(b => b.TokenName == Ticker.ETH.ToString());
// Check USDC balance first
if (usdcBalance?.Value < Constants.GMX.Config.MinimumPositionAmount)
{
await _tradingBot.LogWarning(
$"USDC balance is below {Constants.GMX.Config.MinimumPositionAmount} USD (actual: {usdcBalance?.Value:F2}). Stopping bot {_tradingBot.Identifier}.");
await StopAsync();
return;
}
// Check ETH balance and perform automatic swap if needed
var ethValueInUsd = ethBalance?.Value * ethBalance?.Price ?? 0;
if (ethValueInUsd < 2) // ETH balance below 2 USD
{
await _tradingBot.LogWarning(
$"ETH balance is below 2 USD (actual: {ethValueInUsd:F2}). Attempting to swap USDC to ETH.");
// Check if we have enough USDC for the swap
if (usdcBalance?.Value >= 5) // Need at least 5 USD for swap
{
try
{
var swapInfo = await ServiceScopeHelpers.WithScopedService<IAccountService, SwapInfos>(_scopeFactory, async accountService =>
{
return await accountService.SwapGmxTokensAsync(_state.State.User, _tradingBot.Account.Name, Ticker.USDC, Ticker.ETH, 5);
});
if (swapInfo.Success)
{
await NotifyUserAboutSwap(true, 5, swapInfo.Hash);
}
else
{
await NotifyUserAboutSwap(false, 5, null, swapInfo.Error ?? swapInfo.Message);
await StopAsync();
return;
}
}
catch (Exception ex)
{
await NotifyUserAboutSwap(false, 5, null, ex.Message);
}
}
else
{
// Both USDC and ETH are low - stop the strategy
await _tradingBot.LogWarning(
$"Both USDC ({usdcBalance?.Value:F2}) and ETH ({ethValueInUsd:F2}) balances are low. Stopping bot {_tradingBot.Identifier}.");
await StopAsync();
return;
}
}
// Execute the bot's Run method
await _tradingBot.Run();
SyncStateFromBase();
@@ -680,4 +746,37 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
_logger.LogError(ex, "Failed to save bot statistics for bot {BotId}", _state.State.Identifier);
}
}
/// <summary>
/// Notifies the user about swap operations via webhook/telegram
/// </summary>
private async Task NotifyUserAboutSwap(bool isSuccess, decimal amount, string? transactionHash, string? errorMessage = null)
{
try
{
var message = isSuccess
? $"🔄 **Automatic Swap Successful**\n\n" +
$"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
$"💰 **Amount:** {amount} USDC → ETH\n" +
$"✅ **Status:** Success\n" +
$"🔗 **Transaction:** {transactionHash}\n" +
$"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"
: $"❌ **Automatic Swap Failed**\n\n" +
$"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
$"💰 **Amount:** {amount} USDC → ETH\n" +
$"❌ **Status:** Failed\n" +
$"⚠️ **Error:** {errorMessage}\n" +
$"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";
// Send notification via webhook service
await ServiceScopeHelpers.WithScopedService<IWebhookService>(_scopeFactory, async webhookService =>
{
await webhookService.SendMessage(message, _state.State.User?.TelegramChannel);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send swap notification for bot {BotId}", _tradingBot?.Identifier);
}
}
}

View File

@@ -141,18 +141,6 @@ public class TradingBotBase : ITradingBot
{
if (!Config.IsForBacktest)
{
// Check broker balance before running
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
{
var balance = await exchangeService.GetBalance(Account, false);
if (balance < Constants.GMX.Config.MinimumPositionAmount && Positions.All(p => p.Value.IsFinished()))
{
await LogWarning(
$"Balance on broker is below {Constants.GMX.Config.MinimumPositionAmount} USD (actual: {balance}). Stopping bot {Identifier}.");
return;
}
});
await LoadLastCandle();
}
@@ -1380,7 +1368,7 @@ public class TradingBotBase : ITradingBot
$"🔄 **Watch Mode Toggle**\nBot: `{Config.Name}`\nWatch Only: `{(Config.IsForWatchingOnly ? "ON" : "OFF")}`");
}
private async Task LogInformation(string message)
public async Task LogInformation(string message)
{
Logger.LogInformation(message);
@@ -1397,7 +1385,7 @@ public class TradingBotBase : ITradingBot
}
}
private async Task LogWarning(string message)
public async Task LogWarning(string message)
{
message = $"[{Config.Name}] {message}";
SentrySdk.CaptureException(new Exception(message));