diff --git a/src/Managing.Api/Controllers/UserController.cs b/src/Managing.Api/Controllers/UserController.cs index fcb13ee9..c137ca7a 100644 --- a/src/Managing.Api/Controllers/UserController.cs +++ b/src/Managing.Api/Controllers/UserController.cs @@ -157,6 +157,7 @@ public class UserController : BaseController { var user = await GetUser(); // Map API request to DTO + // Note: IsGmxEnabled and DefaultExchange are not updatable via settings endpoint var settingsDto = new Managing.Application.Abstractions.Models.UserSettingsDto { LowEthAmountAlert = settings.LowEthAmountAlert, @@ -164,12 +165,11 @@ public class UserController : BaseController AutoswapAmount = settings.AutoswapAmount, MaxWaitingTimeForPositionToGetFilledSeconds = settings.MaxWaitingTimeForPositionToGetFilledSeconds, MaxTxnGasFeePerPosition = settings.MaxTxnGasFeePerPosition, - IsGmxEnabled = settings.IsGmxEnabled, + GmxSlippage = settings.GmxSlippage, MinimumConfidence = settings.MinimumConfidence, TrendStrongAgreementThreshold = settings.TrendStrongAgreementThreshold, SignalAgreementThreshold = settings.SignalAgreementThreshold, - AllowSignalTrendOverride = settings.AllowSignalTrendOverride, - DefaultExchange = settings.DefaultExchange + AllowSignalTrendOverride = settings.AllowSignalTrendOverride }; var updatedUser = await _userService.UpdateUserSettings(user, settingsDto); return Ok(updatedUser); diff --git a/src/Managing.Api/Models/Requests/UpdateUserSettingsRequest.cs b/src/Managing.Api/Models/Requests/UpdateUserSettingsRequest.cs index b71b5104..065f3193 100644 --- a/src/Managing.Api/Models/Requests/UpdateUserSettingsRequest.cs +++ b/src/Managing.Api/Models/Requests/UpdateUserSettingsRequest.cs @@ -11,13 +11,14 @@ public class UpdateUserSettingsRequest public decimal? AutoswapAmount { get; set; } public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; } public decimal? MaxTxnGasFeePerPosition { get; set; } - public bool? IsGmxEnabled { get; set; } + // Note: IsGmxEnabled is not updatable via settings endpoint + public decimal? GmxSlippage { get; set; } // Indicator Combo Configuration public Confidence? MinimumConfidence { get; set; } public decimal? TrendStrongAgreementThreshold { get; set; } public decimal? SignalAgreementThreshold { get; set; } public bool? AllowSignalTrendOverride { get; set; } - public TradingExchanges? DefaultExchange { get; set; } + // Note: DefaultExchange is not updatable via settings endpoint } diff --git a/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs b/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs index 17d85a44..a195330a 100644 --- a/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs +++ b/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs @@ -10,13 +10,14 @@ public class UserSettingsDto public decimal? AutoswapAmount { get; set; } public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; } public decimal? MaxTxnGasFeePerPosition { get; set; } - public bool? IsGmxEnabled { get; set; } + // Note: IsGmxEnabled is not updatable via settings endpoint + public decimal? GmxSlippage { get; set; } // Indicator Combo Configuration public Confidence? MinimumConfidence { get; set; } public decimal? TrendStrongAgreementThreshold { get; set; } public decimal? SignalAgreementThreshold { get; set; } public bool? AllowSignalTrendOverride { get; set; } - public TradingExchanges? DefaultExchange { get; set; } + // Note: DefaultExchange is not updatable via settings endpoint } diff --git a/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs b/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs index 980b80f9..d3e54b76 100644 --- a/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs +++ b/src/Managing.Application.Abstractions/Repositories/IEvmManager.cs @@ -38,7 +38,8 @@ public interface IEvmManager Task IncreasePosition(Account account, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage = 1, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null); + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null); Task GetTrade(Account account, string chainName, Ticker ticker); diff --git a/src/Managing.Application.Abstractions/Services/IExchangeService.cs b/src/Managing.Application.Abstractions/Services/IExchangeService.cs index b864b93d..2aca9d67 100644 --- a/src/Managing.Application.Abstractions/Services/IExchangeService.cs +++ b/src/Managing.Application.Abstractions/Services/IExchangeService.cs @@ -21,7 +21,8 @@ public interface IExchangeService DateTime? currentDate = null, bool ioc = true, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null); + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null); Task GetBalance(Account account, bool isForPaperTrading = false); Task GetBalance(Account account, Ticker ticker); diff --git a/src/Managing.Application.Abstractions/Services/ITradingService.cs b/src/Managing.Application.Abstractions/Services/ITradingService.cs index 2cc3ccd1..b31543ff 100644 --- a/src/Managing.Application.Abstractions/Services/ITradingService.cs +++ b/src/Managing.Application.Abstractions/Services/ITradingService.cs @@ -58,7 +58,7 @@ public interface ITradingService Task GetScenarioByNameUserAsync(string scenarioName, User user); Task> GetPositionByUserIdAsync(int userId); Task SwapGmxTokensAsync(User user, string accountName, Ticker fromTicker, Ticker toTicker, - double amount, string orderType = "market", double? triggerRatio = null, double allowedSlippage = 0.5); + double amount, string orderType = "market", double? triggerRatio = null, double? allowedSlippage = null); /// /// Calculates indicator values and generates signals for a given ticker, timeframe, and date range with selected indicators. diff --git a/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs b/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs index 80eab73a..e7e79e5c 100644 --- a/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs +++ b/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs @@ -73,6 +73,10 @@ namespace Managing.Application.Trading.Handlers var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement); var takeProfitPrice = RiskHelpers.GetTakeProfitPrice(request.Direction, openPrice, request.MoneyManagement); + // Get user's slippage setting from IndicatorComboConfig + var config = TradingBox.CreateConfigFromUserSettings(request.User); + var allowedSlippage = request.User.GmxSlippage ?? config.GmxSlippage; + var trade = await exchangeService.OpenTrade( account, request.Ticker, @@ -84,7 +88,8 @@ namespace Managing.Application.Trading.Handlers isForPaperTrading: request.IsForPaperTrading, currentDate: request.Date, stopLossPrice: stopLossPrice, - takeProfitPrice: takeProfitPrice); + takeProfitPrice: takeProfitPrice, + allowedSlippage: allowedSlippage); position.Open = trade; diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs index f89d21a3..11dfac08 100644 --- a/src/Managing.Application/Trading/TradingService.cs +++ b/src/Managing.Application/Trading/TradingService.cs @@ -455,7 +455,7 @@ public class TradingService : ITradingService } public async Task SwapGmxTokensAsync(User user, string accountName, Ticker fromTicker, Ticker toTicker, - double amount, string orderType = "market", double? triggerRatio = null, double allowedSlippage = 0.5) + double amount, string orderType = "market", double? triggerRatio = null, double? allowedSlippage = null) { // Get the account for the user var account = await _accountService.GetAccountByUser(user, accountName, true, false); @@ -473,6 +473,14 @@ public class TradingService : ITradingService try { + // Get user's config to access default slippage value + var config = TradingBox.CreateConfigFromUserSettings(user); + + // Use provided allowedSlippage if specified, otherwise use user's GmxSlippage setting, or default from IndicatorComboConfig + var slippageToUse = (double)(allowedSlippage.HasValue + ? (decimal)allowedSlippage.Value + : (user.GmxSlippage ?? config.GmxSlippage)); + // Call the Web3ProxyService to swap GMX tokens var swapInfos = await _web3ProxyService.SwapGmxTokensAsync( account.Key, @@ -481,7 +489,7 @@ public class TradingService : ITradingService amount, orderType, triggerRatio, - allowedSlippage + slippageToUse ); return swapInfos; diff --git a/src/Managing.Application/Users/UserService.cs b/src/Managing.Application/Users/UserService.cs index d870b090..a101329d 100644 --- a/src/Managing.Application/Users/UserService.cs +++ b/src/Managing.Application/Users/UserService.cs @@ -359,8 +359,10 @@ public class UserService : IUserService if (settings.MaxTxnGasFeePerPosition.HasValue) user.MaxTxnGasFeePerPosition = settings.MaxTxnGasFeePerPosition.Value; - if (settings.IsGmxEnabled.HasValue) - user.IsGmxEnabled = settings.IsGmxEnabled.Value; + // Note: IsGmxEnabled is not updatable via settings endpoint - managed internally + + if (settings.GmxSlippage.HasValue) + user.GmxSlippage = settings.GmxSlippage.Value; if (settings.MinimumConfidence.HasValue) user.MinimumConfidence = settings.MinimumConfidence.Value; @@ -374,8 +376,7 @@ public class UserService : IUserService if (settings.AllowSignalTrendOverride.HasValue) user.AllowSignalTrendOverride = settings.AllowSignalTrendOverride.Value; - if (settings.DefaultExchange.HasValue) - user.DefaultExchange = settings.DefaultExchange.Value; + // Note: DefaultExchange is not updatable via settings endpoint - managed internally await _userRepository.SaveOrUpdateUserAsync(user); return user; diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs index 66a00998..fd784179 100644 --- a/src/Managing.Domain/Shared/Helpers/TradingBox.cs +++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs @@ -58,6 +58,12 @@ public class IndicatorComboConfig /// [Id(5)] public TradingExchanges DefaultExchange { get; set; } = TradingExchanges.GmxV2; + + /// + /// GMX slippage tolerance percentage for trades (default: 0.5%) + /// + [Id(6)] + public decimal GmxSlippage { get; set; } = 0.5m; } public static class TradingBox @@ -80,6 +86,8 @@ public static class TradingBox config.AllowSignalTrendOverride = user.AllowSignalTrendOverride ?? true; // DefaultExchange defaults to GMX if not set config.DefaultExchange = user.DefaultExchange ?? TradingExchanges.GmxV2; + // GmxSlippage defaults to 0.5% if not set + config.GmxSlippage = user.GmxSlippage ?? 0.5m; return config; } diff --git a/src/Managing.Domain/Users/User.cs b/src/Managing.Domain/Users/User.cs index 43c2897e..e60767cc 100644 --- a/src/Managing.Domain/Users/User.cs +++ b/src/Managing.Domain/Users/User.cs @@ -32,6 +32,7 @@ public class User [Id(12)] public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; } [Id(13)] public decimal? MaxTxnGasFeePerPosition { get; set; } [Id(14)] public bool IsGmxEnabled { get; set; } = false; + [Id(20)] public decimal? GmxSlippage { get; set; } // User Settings - Indicator Combo Configuration [Id(15)] public Confidence? MinimumConfidence { get; set; } diff --git a/src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.Designer.cs b/src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.Designer.cs new file mode 100644 index 00000000..fee512f9 --- /dev/null +++ b/src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.Designer.cs @@ -0,0 +1,1788 @@ +// +using System; +using Managing.Infrastructure.Databases.PostgreSql; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Managing.Infrastructure.Databases.Migrations +{ + [DbContext(typeof(ManagingDbContext))] + [Migration("20251230000830_AddGmxSlippageSetting")] + partial class AddGmxSlippageSetting + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.AccountEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exchange") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsGmxInitialized") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Key") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Secret") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.AgentSummaryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActiveStrategiesCount") + .HasColumnType("integer"); + + b.Property("AgentName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("BacktestCount") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Losses") + .HasColumnType("integer"); + + b.Property("NetPnL") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("Runtime") + .HasColumnType("timestamp with time zone"); + + b.Property("TotalBalance") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("TotalFees") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("TotalPnL") + .HasColumnType("decimal(18,8)"); + + b.Property("TotalROI") + .HasColumnType("decimal(18,8)"); + + b.Property("TotalVolume") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("Wins") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AgentName") + .IsUnique(); + + b.HasIndex("TotalPnL"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("AgentSummaries"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BacktestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConfigJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Duration") + .ValueGeneratedOnAdd() + .HasColumnType("interval") + .HasDefaultValue(new TimeSpan(0, 0, 0, 0, 0)); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Fees") + .HasColumnType("decimal(18,8)"); + + b.Property("FinalPnl") + .HasColumnType("decimal(18,8)"); + + b.Property("GrowthPercentage") + .HasColumnType("decimal(18,8)"); + + b.Property("HodlPercentage") + .HasColumnType("decimal(18,8)"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndicatorsCount") + .HasColumnType("integer"); + + b.Property("IndicatorsCsv") + .IsRequired() + .HasColumnType("text"); + + b.Property("InitialBalance") + .HasColumnType("decimal(18,8)"); + + b.Property("MaxDrawdown") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,8)") + .HasDefaultValue(0m); + + b.Property("MaxDrawdownRecoveryTime") + .ValueGeneratedOnAdd() + .HasColumnType("interval") + .HasDefaultValue(new TimeSpan(0, 0, 0, 0, 0)); + + b.Property("Metadata") + .HasColumnType("text"); + + b.Property("MoneyManagementJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NetPnl") + .HasColumnType("decimal(18,8)"); + + b.Property("PositionCount") + .HasColumnType("integer"); + + b.Property("PositionsJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("RequestId") + .HasMaxLength(255) + .HasColumnType("uuid"); + + b.Property("Score") + .HasColumnType("double precision"); + + b.Property("ScoreMessage") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("text"); + + b.Property("SharpeRatio") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,8)") + .HasDefaultValue(0m); + + b.Property("SignalsJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StatisticsJson") + .HasColumnType("jsonb"); + + b.Property("Ticker") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("Timeframe") + .HasColumnType("integer"); + + b.Property("TradingType") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("WinRate") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Identifier") + .IsUnique(); + + b.HasIndex("RequestId"); + + b.HasIndex("Score"); + + b.HasIndex("UserId"); + + b.HasIndex("RequestId", "Score"); + + b.HasIndex("UserId", "Name"); + + b.HasIndex("UserId", "Score"); + + b.HasIndex("UserId", "Ticker"); + + b.HasIndex("UserId", "Timeframe"); + + b.ToTable("Backtests"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BotEntity", b => + { + b.Property("Identifier") + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("uuid"); + + b.Property("AccumulatedRunTimeSeconds") + .HasColumnType("bigint"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Fees") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("LastStartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastStopTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LongPositionCount") + .HasColumnType("integer"); + + b.Property("MasterBotUserId") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NetPnL") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("Pnl") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("Roi") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.Property("ShortPositionCount") + .HasColumnType("integer"); + + b.Property("StartupTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Ticker") + .IsRequired() + .HasColumnType("text"); + + b.Property("TradeLosses") + .HasColumnType("integer"); + + b.Property("TradeWins") + .HasColumnType("integer"); + + b.Property("TradingType") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("Volume") + .HasPrecision(18, 8) + .HasColumnType("numeric(18,8)"); + + b.HasKey("Identifier"); + + b.HasIndex("Identifier") + .IsUnique(); + + b.HasIndex("MasterBotUserId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId"); + + b.ToTable("Bots"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BundleBacktestRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CompletedBacktests") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CurrentBacktest") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("DateTimeRangesJson") + .IsRequired() + .HasColumnType("text"); + + b.Property("ErrorMessage") + .HasColumnType("text"); + + b.Property("EstimatedTimeRemainingSeconds") + .HasColumnType("integer"); + + b.Property("FailedBacktests") + .HasColumnType("integer"); + + b.Property("MoneyManagementVariantsJson") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ProgressInfo") + .HasColumnType("text"); + + b.Property("RequestId") + .HasMaxLength(255) + .HasColumnType("uuid"); + + b.Property("ResultsJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("TickerVariantsJson") + .IsRequired() + .HasColumnType("text"); + + b.Property("TotalBacktests") + .HasColumnType("integer"); + + b.Property("UniversalConfigJson") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("RequestId") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "CreatedAt"); + + b.HasIndex("UserId", "Name", "Version"); + + b.ToTable("BundleBacktestRequests"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.FundingRateEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("Exchange") + .HasColumnType("integer"); + + b.Property("OpenInterest") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.Property("Rate") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.Property("Ticker") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Date"); + + b.HasIndex("Exchange"); + + b.HasIndex("Ticker"); + + b.HasIndex("Exchange", "Date"); + + b.HasIndex("Ticker", "Exchange"); + + b.HasIndex("Ticker", "Exchange", "Date") + .IsUnique(); + + b.ToTable("FundingRates"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.GeneticRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Balance") + .HasColumnType("decimal(18,8)"); + + b.Property("BestChromosome") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("BestFitness") + .HasColumnType("double precision"); + + b.Property("BestFitnessSoFar") + .HasColumnType("double precision"); + + b.Property("BestIndividual") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrossoverMethod") + .IsRequired() + .HasColumnType("text"); + + b.Property("CurrentGeneration") + .HasColumnType("integer"); + + b.Property("EligibleIndicatorsJson") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ElitismPercentage") + .HasColumnType("integer"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Generations") + .HasColumnType("integer"); + + b.Property("MaxTakeProfit") + .HasColumnType("double precision"); + + b.Property("MutationMethod") + .IsRequired() + .HasColumnType("text"); + + b.Property("MutationRate") + .HasColumnType("double precision"); + + b.Property("PopulationSize") + .HasColumnType("integer"); + + b.Property("ProgressInfo") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SelectionMethod") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Ticker") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timeframe") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RequestId") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("UserId"); + + b.ToTable("GeneticRequests"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.IndicatorEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CyclePeriods") + .HasColumnType("integer"); + + b.Property("FastPeriods") + .HasColumnType("integer"); + + b.Property("MinimumHistory") + .HasColumnType("integer"); + + b.Property("Multiplier") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Period") + .HasColumnType("integer"); + + b.Property("SignalPeriods") + .HasColumnType("integer"); + + b.Property("SignalType") + .IsRequired() + .HasColumnType("text"); + + b.Property("SlowPeriods") + .HasColumnType("integer"); + + b.Property("SmoothPeriods") + .HasColumnType("integer"); + + b.Property("StochPeriods") + .HasColumnType("integer"); + + b.Property("Timeframe") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Name"); + + b.ToTable("Indicators"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.JobEntity", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AssignedWorkerId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("BundleRequestId") + .HasColumnType("uuid"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ConfigJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ErrorMessage") + .HasColumnType("text"); + + b.Property("FailureCategory") + .HasColumnType("integer"); + + b.Property("GeneticRequestId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsRetryable") + .HasColumnType("boolean"); + + b.Property("JobType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("LastHeartbeat") + .HasColumnType("timestamp with time zone"); + + b.Property("MaxRetries") + .HasColumnType("integer"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("ProgressPercentage") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("RequestId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ResultJson") + .HasColumnType("jsonb"); + + b.Property("RetryAfter") + .HasColumnType("timestamp with time zone"); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BundleRequestId") + .HasDatabaseName("idx_bundle_request"); + + b.HasIndex("GeneticRequestId") + .HasDatabaseName("idx_genetic_request"); + + b.HasIndex("AssignedWorkerId", "Status") + .HasDatabaseName("idx_assigned_worker"); + + b.HasIndex("UserId", "Status") + .HasDatabaseName("idx_user_status"); + + b.HasIndex("Status", "JobType", "Priority", "CreatedAt") + .HasDatabaseName("idx_status_jobtype_priority_created"); + + b.ToTable("Jobs", (string)null); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.MoneyManagementEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Leverage") + .HasColumnType("decimal(18,8)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("StopLoss") + .HasColumnType("decimal(18,8)"); + + b.Property("TakeProfit") + .HasColumnType("decimal(18,8)"); + + b.Property("Timeframe") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("UserName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserName"); + + b.HasIndex("UserName", "Name"); + + b.ToTable("MoneyManagements"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.PositionEntity", b => + { + b.Property("Identifier") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("GasFees") + .HasColumnType("decimal(18,8)"); + + b.Property("Initiator") + .IsRequired() + .HasColumnType("text"); + + b.Property("InitiatorIdentifier") + .HasColumnType("uuid"); + + b.Property("MoneyManagementJson") + .HasColumnType("text"); + + b.Property("NetPnL") + .HasColumnType("decimal(18,8)"); + + b.Property("OpenTradeId") + .HasColumnType("integer"); + + b.Property("OriginDirection") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProfitAndLoss") + .HasColumnType("decimal(18,8)"); + + b.Property("SignalIdentifier") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("StopLossTradeId") + .HasColumnType("integer"); + + b.Property("TakeProfit1TradeId") + .HasColumnType("integer"); + + b.Property("TakeProfit2TradeId") + .HasColumnType("integer"); + + b.Property("Ticker") + .IsRequired() + .HasColumnType("text"); + + b.Property("TradingType") + .HasColumnType("integer"); + + b.Property("UiFees") + .HasColumnType("decimal(18,8)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Identifier"); + + b.HasIndex("Identifier") + .IsUnique(); + + b.HasIndex("InitiatorIdentifier"); + + b.HasIndex("OpenTradeId"); + + b.HasIndex("Status"); + + b.HasIndex("StopLossTradeId"); + + b.HasIndex("TakeProfit1TradeId"); + + b.HasIndex("TakeProfit2TradeId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Identifier"); + + b.ToTable("Positions"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.ScenarioEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LoopbackPeriod") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Name"); + + b.ToTable("Scenarios"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.ScenarioIndicatorEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IndicatorId") + .HasColumnType("integer"); + + b.Property("ScenarioId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IndicatorId"); + + b.HasIndex("ScenarioId", "IndicatorId") + .IsUnique(); + + b.ToTable("ScenarioIndicators"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.SignalEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CandleJson") + .HasColumnType("text"); + + b.Property("Confidence") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Direction") + .IsRequired() + .HasColumnType("text"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndicatorName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SignalType") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Ticker") + .IsRequired() + .HasColumnType("text"); + + b.Property("Timeframe") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Date"); + + b.HasIndex("Identifier"); + + b.HasIndex("Status"); + + b.HasIndex("Ticker"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Date"); + + b.HasIndex("Identifier", "Date", "UserId") + .IsUnique(); + + b.ToTable("Signals"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.SpotlightOverviewEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Identifier") + .HasColumnType("uuid"); + + b.Property("ScenarioCount") + .HasColumnType("integer"); + + b.Property("SpotlightsJson") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DateTime"); + + b.HasIndex("Identifier") + .IsUnique(); + + b.HasIndex("DateTime", "ScenarioCount"); + + b.ToTable("SpotlightOverviews"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.SynthMinersLeaderboardEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Asset") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("CacheKey") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsBacktest") + .HasColumnType("boolean"); + + b.Property("MinersData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SignalDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeIncrement") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CacheKey") + .IsUnique(); + + b.ToTable("SynthMinersLeaderboards"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.SynthPredictionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Asset") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("CacheKey") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsBacktest") + .HasColumnType("boolean"); + + b.Property("MinerUid") + .HasColumnType("integer"); + + b.Property("PredictionData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SignalDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeIncrement") + .HasColumnType("integer"); + + b.Property("TimeLength") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CacheKey") + .IsUnique(); + + b.ToTable("SynthPredictions"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.TopVolumeTickerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Exchange") + .HasColumnType("integer"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("Ticker") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Volume") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.HasKey("Id"); + + b.HasIndex("Date"); + + b.HasIndex("Exchange"); + + b.HasIndex("Ticker"); + + b.HasIndex("Date", "Rank"); + + b.HasIndex("Exchange", "Date"); + + b.ToTable("TopVolumeTickers"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.TradeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Direction") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExchangeOrderId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Leverage") + .HasColumnType("decimal(18,8)"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("decimal(18,8)"); + + b.Property("Quantity") + .HasColumnType("decimal(18,8)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Ticker") + .IsRequired() + .HasColumnType("text"); + + b.Property("TradeType") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Date"); + + b.HasIndex("ExchangeOrderId"); + + b.HasIndex("Status"); + + b.ToTable("Trades"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.TraderEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AverageLoss") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.Property("AverageWin") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsBestTrader") + .HasColumnType("boolean"); + + b.Property("Pnl") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.Property("Roi") + .HasPrecision(18, 8) + .HasColumnType("decimal(18,8)"); + + b.Property("TradeCount") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Winrate") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Address"); + + b.HasIndex("IsBestTrader"); + + b.HasIndex("Pnl"); + + b.HasIndex("Roi"); + + b.HasIndex("Winrate"); + + b.HasIndex("Address", "IsBestTrader") + .IsUnique(); + + b.HasIndex("IsBestTrader", "Roi"); + + b.HasIndex("IsBestTrader", "Winrate"); + + b.ToTable("Traders"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AgentName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AllowSignalTrendOverride") + .HasColumnType("boolean"); + + b.Property("AutoswapAmount") + .HasColumnType("decimal(18,8)"); + + b.Property("AvatarUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("DefaultExchange") + .HasColumnType("text"); + + b.Property("EnableAutoswap") + .HasColumnType("boolean"); + + b.Property("GmxSlippage") + .HasColumnType("decimal(5,2)"); + + b.Property("IsAdmin") + .HasColumnType("boolean"); + + b.Property("IsGmxEnabled") + .HasColumnType("boolean"); + + b.Property("LastConnectionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LowEthAmountAlert") + .HasColumnType("decimal(18,8)"); + + b.Property("MaxTxnGasFeePerPosition") + .HasColumnType("decimal(18,8)"); + + b.Property("MaxWaitingTimeForPositionToGetFilledSeconds") + .HasColumnType("integer"); + + b.Property("MinimumConfidence") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("OwnerWalletAddress") + .HasColumnType("text"); + + b.Property("SignalAgreementThreshold") + .HasColumnType("decimal(5,4)"); + + b.Property("TelegramChannel") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TrendStrongAgreementThreshold") + .HasColumnType("decimal(5,4)"); + + b.HasKey("Id"); + + b.HasIndex("AgentName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.WhitelistAccountEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmbeddedWallet") + .IsRequired() + .HasMaxLength(42) + .HasColumnType("character varying(42)"); + + b.Property("ExternalEthereumAccount") + .HasMaxLength(42) + .HasColumnType("character varying(42)"); + + b.Property("IsWhitelisted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("PrivyCreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivyId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TwitterAccount") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("EmbeddedWallet") + .IsUnique(); + + b.HasIndex("ExternalEthereumAccount"); + + b.HasIndex("PrivyId") + .IsUnique(); + + b.HasIndex("TwitterAccount"); + + b.ToTable("WhitelistAccounts"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.WorkerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DelayTicks") + .HasColumnType("bigint"); + + b.Property("ExecutionCount") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("LastRunTime") + .HasColumnType("timestamp with time zone"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("WorkerType") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("WorkerType") + .IsUnique(); + + b.ToTable("Workers"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.AccountEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany("Accounts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.AgentSummaryEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BacktestEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BotEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "MasterBotUser") + .WithMany() + .HasForeignKey("MasterBotUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("MasterBotUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BundleBacktestRequestEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.GeneticRequestEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.IndicatorEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.JobEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.MoneyManagementEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.PositionEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.TradeEntity", "OpenTrade") + .WithMany() + .HasForeignKey("OpenTradeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.TradeEntity", "StopLossTrade") + .WithMany() + .HasForeignKey("StopLossTradeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.TradeEntity", "TakeProfit1Trade") + .WithMany() + .HasForeignKey("TakeProfit1TradeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.TradeEntity", "TakeProfit2Trade") + .WithMany() + .HasForeignKey("TakeProfit2TradeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("OpenTrade"); + + b.Navigation("StopLossTrade"); + + b.Navigation("TakeProfit1Trade"); + + b.Navigation("TakeProfit2Trade"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.ScenarioEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.ScenarioIndicatorEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.IndicatorEntity", "Indicator") + .WithMany("ScenarioIndicators") + .HasForeignKey("IndicatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.ScenarioEntity", "Scenario") + .WithMany("ScenarioIndicators") + .HasForeignKey("ScenarioId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Indicator"); + + b.Navigation("Scenario"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.SignalEntity", b => + { + b.HasOne("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.IndicatorEntity", b => + { + b.Navigation("ScenarioIndicators"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.ScenarioEntity", b => + { + b.Navigation("ScenarioIndicators"); + }); + + modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.UserEntity", b => + { + b.Navigation("Accounts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.cs b/src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.cs new file mode 100644 index 00000000..790e6dfc --- /dev/null +++ b/src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Managing.Infrastructure.Databases.Migrations +{ + /// + public partial class AddGmxSlippageSetting : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Add column with default value + migrationBuilder.AddColumn( + name: "GmxSlippage", + table: "Users", + type: "numeric(5,2)", + nullable: true, + defaultValue: 0.5m); + + // Update existing NULL values to default + migrationBuilder.Sql(@" + UPDATE ""Users"" + SET ""GmxSlippage"" = 0.5 + WHERE ""GmxSlippage"" IS NULL; + "); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "GmxSlippage", + table: "Users"); + } + } +} diff --git a/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs b/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs index 7e8f923a..f5497b73 100644 --- a/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs +++ b/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs @@ -1441,6 +1441,9 @@ namespace Managing.Infrastructure.Databases.Migrations b.Property("EnableAutoswap") .HasColumnType("boolean"); + b.Property("GmxSlippage") + .HasColumnType("decimal(5,2)"); + b.Property("IsAdmin") .HasColumnType("boolean"); diff --git a/src/Managing.Infrastructure.Database/PostgreSql/Entities/UserEntity.cs b/src/Managing.Infrastructure.Database/PostgreSql/Entities/UserEntity.cs index a1c0fb26..66ece3be 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/Entities/UserEntity.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/Entities/UserEntity.cs @@ -26,6 +26,7 @@ public class UserEntity public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; } = 600; // Default: 10 minutes (600 seconds) [Column(TypeName = "decimal(18,8)")] public decimal? MaxTxnGasFeePerPosition { get; set; } = 1.5m; // Default: MaximumGasFeeUsd public bool IsGmxEnabled { get; set; } = false; + [Column(TypeName = "decimal(5,2)")] public decimal? GmxSlippage { get; set; } = 0.5m; // Default: 0.5% slippage for GMX trades // User Settings - Indicator Combo Configuration public Confidence? MinimumConfidence { get; set; } = Confidence.Medium; // Default: Medium confidence for context indicators diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs index fe08e9dd..6078b297 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs @@ -140,6 +140,7 @@ public static class PostgreSqlMappers MaxWaitingTimeForPositionToGetFilledSeconds = entity.MaxWaitingTimeForPositionToGetFilledSeconds, MaxTxnGasFeePerPosition = entity.MaxTxnGasFeePerPosition, IsGmxEnabled = entity.IsGmxEnabled, + GmxSlippage = entity.GmxSlippage, MinimumConfidence = entity.MinimumConfidence, TrendStrongAgreementThreshold = entity.TrendStrongAgreementThreshold, SignalAgreementThreshold = entity.SignalAgreementThreshold, @@ -187,6 +188,7 @@ public static class PostgreSqlMappers MaxWaitingTimeForPositionToGetFilledSeconds = user.MaxWaitingTimeForPositionToGetFilledSeconds, MaxTxnGasFeePerPosition = user.MaxTxnGasFeePerPosition, IsGmxEnabled = user.IsGmxEnabled, + GmxSlippage = user.GmxSlippage, MinimumConfidence = user.MinimumConfidence, TrendStrongAgreementThreshold = user.TrendStrongAgreementThreshold, SignalAgreementThreshold = user.SignalAgreementThreshold, diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs index 3d12d1af..22b210b1 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlUserRepository.cs @@ -263,6 +263,7 @@ public class PostgreSqlUserRepository : BaseRepositoryWithLogging, IUserReposito existingUser.MaxWaitingTimeForPositionToGetFilledSeconds = user.MaxWaitingTimeForPositionToGetFilledSeconds; existingUser.MaxTxnGasFeePerPosition = user.MaxTxnGasFeePerPosition; existingUser.IsGmxEnabled = user.IsGmxEnabled; + existingUser.GmxSlippage = user.GmxSlippage; existingUser.MinimumConfidence = user.MinimumConfidence; existingUser.TrendStrongAgreementThreshold = user.TrendStrongAgreementThreshold; existingUser.SignalAgreementThreshold = user.SignalAgreementThreshold; diff --git a/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs b/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs index 442ffa3b..f78965d4 100644 --- a/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs +++ b/src/Managing.Infrastructure.Exchanges/Abstractions/IExchangeProcessor.cs @@ -23,7 +23,8 @@ public interface IExchangeProcessor DateTime? currentDate = null, bool ioc = true, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null); + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null); Task GetBalance(Account account, bool isForPaperTrading = false); Task> GetBalances(Account account, bool isForPaperTrading = false); Task GetPrice(Account account, Ticker ticker, DateTime date); diff --git a/src/Managing.Infrastructure.Exchanges/ExchangeService.cs b/src/Managing.Infrastructure.Exchanges/ExchangeService.cs index f95f178a..1a45f082 100644 --- a/src/Managing.Infrastructure.Exchanges/ExchangeService.cs +++ b/src/Managing.Infrastructure.Exchanges/ExchangeService.cs @@ -46,10 +46,11 @@ namespace Managing.Infrastructure.Exchanges DateTime? currentDate = null, bool ioc = true, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null) + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null) { _logger.LogDebug( - $"OpenMarketTrade - {ticker} - Type: {tradeType} - {direction} - Price: {price} - Quantity: {quantity} - Leverage: {leverage} - SL: {stopLossPrice} - TP: {takeProfitPrice}"); + $"OpenMarketTrade - {ticker} - Type: {tradeType} - {direction} - Price: {price} - Quantity: {quantity} - Leverage: {leverage} - SL: {stopLossPrice} - TP: {takeProfitPrice} - Slippage: {allowedSlippage}"); if (isForPaperTrading) { @@ -60,7 +61,7 @@ namespace Managing.Infrastructure.Exchanges var processor = GetProcessor(account); return await processor.OpenTrade(account, ticker, direction, price, quantity, leverage, tradeType, - reduceOnly, isForPaperTrading, currentDate, ioc, stopLossPrice, takeProfitPrice); + reduceOnly, isForPaperTrading, currentDate, ioc, stopLossPrice, takeProfitPrice, allowedSlippage); } private IExchangeProcessor GetProcessor(Account account) diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs index 4e5fe258..937aac5f 100644 --- a/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/BaseProcessor.cs @@ -35,7 +35,8 @@ namespace Managing.Infrastructure.Exchanges.Exchanges DateTime? currentDate = null, bool ioc = true, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null); + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null); public abstract Orderbook GetOrderbook(Account account, Ticker ticker); public abstract Task> GetBalances(Account account, bool isForPaperTrading = false); public abstract Task> GetOrders(Account account, Ticker ticker); diff --git a/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs b/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs index a153c333..4c648a75 100644 --- a/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs +++ b/src/Managing.Infrastructure.Exchanges/Exchanges/EvmProcessor.cs @@ -174,7 +174,8 @@ public class EvmProcessor : BaseProcessor DateTime? currentDate = null, bool ioc = true, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null) + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null) { Trade trade; if (reduceOnly) @@ -197,7 +198,7 @@ public class EvmProcessor : BaseProcessor else { trade = await _evmManager.IncreasePosition(account, ticker, direction, price, quantity, leverage, - stopLossPrice, takeProfitPrice); + stopLossPrice, takeProfitPrice, allowedSlippage); } return trade; diff --git a/src/Managing.Infrastructure.Web3/EvmManager.cs b/src/Managing.Infrastructure.Web3/EvmManager.cs index b9350abc..ba63a88c 100644 --- a/src/Managing.Infrastructure.Web3/EvmManager.cs +++ b/src/Managing.Infrastructure.Web3/EvmManager.cs @@ -641,7 +641,8 @@ public class EvmManager : IEvmManager decimal quantity, decimal? leverage, decimal? stopLossPrice = null, - decimal? takeProfitPrice = null) + decimal? takeProfitPrice = null, + decimal? allowedSlippage = null) { Trade trade = null; @@ -664,7 +665,8 @@ public class EvmManager : IEvmManager quantity, leverage = leverage ?? 1.0m, stopLossPrice = stopLossPrice, - takeProfitPrice = takeProfitPrice + takeProfitPrice = takeProfitPrice, + allowedSlippage = allowedSlippage.HasValue ? (double)allowedSlippage.Value : (double?)null }); // Create a trade object using the returned hash diff --git a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts index 62e968fc..01acb37b 100644 --- a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts +++ b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts @@ -643,7 +643,8 @@ const openPositionSchema = z.object({ direction: z.enum(Object.keys(TradeDirection) as [string, ...string[]]), price: z.number().positive().optional(), quantity: z.number().positive(), - leverage: z.number().positive() + leverage: z.number().positive(), + allowedSlippage: z.number().min(0).max(100).optional() }); // Schema for cancel-orders request @@ -774,7 +775,8 @@ export const openGmxPositionImpl = async ( leverage: number, price?: number, stopLossPrice?: number, - takeProfitPrice?: number + takeProfitPrice?: number, + allowedSlippage?: number ): Promise => { return executeWithFallback( async (sdk, retryCount) => { @@ -852,12 +854,18 @@ export const openGmxPositionImpl = async ( const leverageBps = BigInt((leverage || 1) * 10000); const limitPrice = price ? numberToBigint(price, 30) : undefined; + // Use provided slippage or default to 0.5% (50 basis points) + // Convert percentage to basis points (e.g., 0.5% = 50 bps) + const slippageBps = allowedSlippage != null + ? Math.floor(allowedSlippage * 100) // Convert percentage to basis points + : 50; // Default 0.5% = 50 basis points + const params: PositionIncreaseParams = { payAmount: collateralAmount, marketAddress: marketInfo.marketTokenAddress, payTokenAddress: collateralToken.address, collateralTokenAddress: collateralToken.address, - allowedSlippageBps: 55, // 0.55% slippage + allowedSlippageBps: slippageBps, leverage: leverageBps, skipSimulation: true, referralCodeForTxn: encodeReferralCode("kaigen_ai"), @@ -971,7 +979,8 @@ export async function openGmxPosition( quantity?: number, leverage?: number, stopLossPrice?: number, - takeProfitPrice?: number + takeProfitPrice?: number, + allowedSlippage?: number ) { try { // Validate the request parameters @@ -984,7 +993,8 @@ export async function openGmxPosition( quantity, leverage, stopLossPrice, - takeProfitPrice + takeProfitPrice, + allowedSlippage }); // Get client for the address @@ -999,7 +1009,8 @@ export async function openGmxPosition( leverage, price, stopLossPrice, - takeProfitPrice + takeProfitPrice, + allowedSlippage ); return {