From 21d87efeee8011711601838a6170eddb3f0e0c81 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Tue, 30 Dec 2025 07:19:08 +0700 Subject: [PATCH] Refactor user settings management to remove IsGmxEnabled and DefaultExchange from updatable fields, introducing GmxSlippage instead. Update UserController, UserService, and related DTOs to reflect these changes, ensuring proper handling of user settings. Adjust database schema and migrations to accommodate the new GmxSlippage property, enhancing user customization options for trading configurations. --- .../Controllers/UserController.cs | 6 +- .../Requests/UpdateUserSettingsRequest.cs | 5 +- .../Models/UserSettingsDto.cs | 5 +- .../Repositories/IEvmManager.cs | 3 +- .../Services/IExchangeService.cs | 3 +- .../Services/ITradingService.cs | 2 +- .../Handlers/OpenPositionCommandHandler.cs | 7 +- .../Trading/TradingService.cs | 12 +- src/Managing.Application/Users/UserService.cs | 9 +- .../Shared/Helpers/TradingBox.cs | 8 + src/Managing.Domain/Users/User.cs | 1 + ...30000830_AddGmxSlippageSetting.Designer.cs | 1788 +++++++++++++++++ .../20251230000830_AddGmxSlippageSetting.cs | 37 + .../ManagingDbContextModelSnapshot.cs | 3 + .../PostgreSql/Entities/UserEntity.cs | 1 + .../PostgreSql/PostgreSqlMappers.cs | 2 + .../PostgreSql/PostgreSqlUserRepository.cs | 1 + .../Abstractions/IExchangeProcessor.cs | 3 +- .../ExchangeService.cs | 7 +- .../Exchanges/BaseProcessor.cs | 3 +- .../Exchanges/EvmProcessor.cs | 5 +- .../EvmManager.cs | 6 +- .../src/plugins/custom/gmx.ts | 23 +- 23 files changed, 1908 insertions(+), 32 deletions(-) create mode 100644 src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.Designer.cs create mode 100644 src/Managing.Infrastructure.Database/Migrations/20251230000830_AddGmxSlippageSetting.cs 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 {