Enhance user settings management by adding new properties and updating related functionality

This commit introduces additional user settings properties, including TrendStrongAgreementThreshold, SignalAgreementThreshold, AllowSignalTrendOverride, and DefaultExchange, to the User entity and associated DTOs. The UserController and UserService are updated to handle these new settings, allowing users to customize their trading configurations more effectively. Database migrations are also included to ensure proper schema updates for the new fields.
This commit is contained in:
2025-12-30 06:48:08 +07:00
parent 79d8a381d9
commit aa3b06bbe4
26 changed files with 5909 additions and 57 deletions

View File

@@ -2,6 +2,7 @@ using Managing.Common;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Indicators;
using Managing.Domain.Shared.Helpers;
namespace Managing.Application.Abstractions.Grains;
@@ -17,8 +18,11 @@ public interface IScenarioRunnerGrain : IGrainWithGuidKey
/// <param name="config">The trading bot configuration</param>
/// <param name="previousSignals">Previous signals to consider</param>
/// <param name="tradingExchange">Trading Exchange</param>
/// <param name="lastCandle">The last candle</param>
/// <param name="indicatorComboConfig">Optional indicator combo configuration (for user settings)</param>
/// <returns>The generated signal or null if no signal</returns>
Task<LightSignal> GetSignals(TradingBotConfig config, Dictionary<string, LightSignal> previousSignals,
Enums.TradingExchanges tradingExchange,
Candle lastCandle);
Candle lastCandle,
IndicatorComboConfig indicatorComboConfig = null);
}

View File

@@ -714,7 +714,11 @@ public class FuturesBot : TradingBotBase
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
{
var scenarioRunnerGrain = grainFactory.GetGrain<IScenarioRunnerGrain>(Guid.NewGuid());
var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle);
// Create indicator combo config from user settings
var indicatorComboConfig = TradingBox.CreateConfigFromUserSettings(Account.User);
var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle, indicatorComboConfig);
if (signal == null) return;
await AddSignal(signal);
});

View File

@@ -313,6 +313,10 @@ public class AgentGrain : Grain, IAgentGrain
public async Task<BalanceCheckResult> CheckAndEnsureEthBalanceAsync(Guid requestingBotId, string accountName)
{
// Get user settings
var userId = (int)this.GetPrimaryKeyLong();
var user = await _userService.GetUserByIdAsync(userId);
// Check if a swap is already in progress
if (_state.State.IsSwapInProgress)
{
@@ -358,6 +362,15 @@ public class AgentGrain : Grain, IAgentGrain
};
}
// Check low ETH amount alert threshold
var lowEthAlertThreshold = user.LowEthAmountAlert ?? Constants.GMX.Config.MinimumTradeEthBalanceUsd;
if (balanceData.EthValueInUsd < lowEthAlertThreshold)
{
_logger.LogWarning(
"ETH balance below alert threshold for user {UserId} - ETH: {EthValue:F2} USD (threshold: {Threshold:F2} USD)",
userId, balanceData.EthValueInUsd, lowEthAlertThreshold);
}
_logger.LogInformation(
"Agent {UserId} balance check - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (cached: {IsCached})",
this.GetPrimaryKeyLong(), balanceData.EthValueInUsd, balanceData.UsdcValue,
@@ -402,18 +415,34 @@ public class AgentGrain : Grain, IAgentGrain
};
}
// Check if we have enough USDC for swap (need at least 5 USD for swap)
if (balanceData.UsdcValue <
(Constants.GMX.Config.MinimumPositionAmount + (decimal)Constants.GMX.Config.AutoSwapAmount))
// Check if autoswap is enabled for this user
if (!user.EnableAutoswap)
{
_logger.LogInformation("Autoswap is disabled for user {UserId}, skipping swap",
userId);
return new BalanceCheckResult
{
IsSuccessful = false,
FailureReason = BalanceCheckFailureReason.None,
Message = "Autoswap is disabled for this user",
ShouldStopBot = false
};
}
// Get autoswap amount from user settings or use default
var autoswapAmount = user.AutoswapAmount ?? (decimal)Constants.GMX.Config.AutoSwapAmount;
// Check if we have enough USDC for swap
if (balanceData.UsdcValue < (Constants.GMX.Config.MinimumPositionAmount + autoswapAmount))
{
_logger.LogWarning(
"Insufficient USDC balance for swap - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (need {AutoSwapAmount} USD for swap)",
balanceData.EthValueInUsd, balanceData.UsdcValue, Constants.GMX.Config.AutoSwapAmount);
balanceData.EthValueInUsd, balanceData.UsdcValue, autoswapAmount);
return new BalanceCheckResult
{
IsSuccessful = false,
FailureReason = BalanceCheckFailureReason.InsufficientUsdcForSwap,
Message = $"Insufficient USDC balance for swap (need {Constants.GMX.Config.AutoSwapAmount} USD)",
Message = $"Insufficient USDC balance for swap (need {autoswapAmount} USD)",
ShouldStopBot = true
};
}
@@ -440,22 +469,18 @@ public class AgentGrain : Grain, IAgentGrain
try
{
_logger.LogInformation("Initiating USDC to ETH swap for agent {UserId} - swapping 5 USDC",
this.GetPrimaryKeyLong());
_logger.LogInformation("Initiating USDC to ETH swap for agent {UserId} - swapping {Amount} USDC",
this.GetPrimaryKeyLong(), autoswapAmount);
// Get user for the swap
var userId = (int)this.GetPrimaryKeyLong();
var user = await _userService.GetUserByIdAsync(userId);
// Perform the swap
// Perform the swap using user's autoswap amount
var swapInfo = await _tradingService.SwapGmxTokensAsync(user, accountName,
Ticker.USDC, Ticker.ETH, Constants.GMX.Config.AutoSwapAmount);
Ticker.USDC, Ticker.ETH, (double)autoswapAmount);
if (swapInfo.Success)
{
_logger.LogInformation(
"Successfully swapped 5 USDC to ETH for agent {UserId}, transaction hash: {Hash}",
userId, swapInfo.Hash);
"Successfully swapped {Amount} USDC to ETH for agent {UserId}, transaction hash: {Hash}",
autoswapAmount, userId, swapInfo.Hash);
// Update last swap time and invalidate cache
_state.State.LastSwapTime = DateTime.UtcNow;

View File

@@ -860,7 +860,11 @@ public class SpotBot : TradingBotBase
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
{
var scenarioRunnerGrain = grainFactory.GetGrain<IScenarioRunnerGrain>(Guid.NewGuid());
var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle);
// Create indicator combo config from user settings
var indicatorComboConfig = TradingBox.CreateConfigFromUserSettings(Account.User);
var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle, indicatorComboConfig);
if (signal == null) return;
await AddSignal(signal);
});

View File

@@ -56,7 +56,7 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain
}
public async Task<LightSignal> GetSignals(TradingBotConfig config, Dictionary<string, LightSignal> previousSignals,
TradingExchanges tradingExchanges, Candle candle)
TradingExchanges tradingExchanges, Candle candle, IndicatorComboConfig indicatorComboConfig = null)
{
try
{
@@ -84,11 +84,16 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain
_logger.LogInformation($"Fetched {candlesList.Count} candles for {config.Ticker} for {config.Name}");
// Use provided config or default
var comboConfig = indicatorComboConfig ?? new IndicatorComboConfig();
var signal = TradingBox.GetSignal(
candlesList,
config.Scenario,
previousSignals,
config.Scenario?.LookbackPeriod ?? 1);
comboConfig,
config.Scenario?.LookbackPeriod ?? 1,
null);
if (signal != null && signal.Date > candle.Date)
{

View File

@@ -365,6 +365,18 @@ public class UserService : IUserService
if (settings.MinimumConfidence.HasValue)
user.MinimumConfidence = settings.MinimumConfidence.Value;
if (settings.TrendStrongAgreementThreshold.HasValue)
user.TrendStrongAgreementThreshold = settings.TrendStrongAgreementThreshold.Value;
if (settings.SignalAgreementThreshold.HasValue)
user.SignalAgreementThreshold = settings.SignalAgreementThreshold.Value;
if (settings.AllowSignalTrendOverride.HasValue)
user.AllowSignalTrendOverride = settings.AllowSignalTrendOverride.Value;
if (settings.DefaultExchange.HasValue)
user.DefaultExchange = settings.DefaultExchange.Value;
await _userRepository.SaveOrUpdateUserAsync(user);
return user;
}