diff --git a/src/Managing.Api/Controllers/UserController.cs b/src/Managing.Api/Controllers/UserController.cs
index f61a96a7..fcb13ee9 100644
--- a/src/Managing.Api/Controllers/UserController.cs
+++ b/src/Managing.Api/Controllers/UserController.cs
@@ -165,7 +165,11 @@ public class UserController : BaseController
MaxWaitingTimeForPositionToGetFilledSeconds = settings.MaxWaitingTimeForPositionToGetFilledSeconds,
MaxTxnGasFeePerPosition = settings.MaxTxnGasFeePerPosition,
IsGmxEnabled = settings.IsGmxEnabled,
- MinimumConfidence = settings.MinimumConfidence
+ MinimumConfidence = settings.MinimumConfidence,
+ TrendStrongAgreementThreshold = settings.TrendStrongAgreementThreshold,
+ SignalAgreementThreshold = settings.SignalAgreementThreshold,
+ AllowSignalTrendOverride = settings.AllowSignalTrendOverride,
+ DefaultExchange = settings.DefaultExchange
};
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 77d0819d..b71b5104 100644
--- a/src/Managing.Api/Models/Requests/UpdateUserSettingsRequest.cs
+++ b/src/Managing.Api/Models/Requests/UpdateUserSettingsRequest.cs
@@ -5,12 +5,19 @@ namespace Managing.Api.Models.Requests;
public class UpdateUserSettingsRequest
{
+ // Trading Configuration
public decimal? LowEthAmountAlert { get; set; }
public bool? EnableAutoswap { get; set; }
public decimal? AutoswapAmount { get; set; }
public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; }
public decimal? MaxTxnGasFeePerPosition { get; set; }
public bool? IsGmxEnabled { 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; }
}
diff --git a/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs b/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs
index 9e9d92b0..17d85a44 100644
--- a/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs
+++ b/src/Managing.Application.Abstractions/Models/UserSettingsDto.cs
@@ -4,12 +4,19 @@ namespace Managing.Application.Abstractions.Models;
public class UserSettingsDto
{
+ // Trading Configuration
public decimal? LowEthAmountAlert { get; set; }
public bool? EnableAutoswap { get; set; }
public decimal? AutoswapAmount { get; set; }
public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; }
public decimal? MaxTxnGasFeePerPosition { get; set; }
public bool? IsGmxEnabled { 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; }
}
diff --git a/src/Managing.Application/Abstractions/Grains/IScenarioRunnerGrain.cs b/src/Managing.Application/Abstractions/Grains/IScenarioRunnerGrain.cs
index 2f05300a..1b7d080c 100644
--- a/src/Managing.Application/Abstractions/Grains/IScenarioRunnerGrain.cs
+++ b/src/Managing.Application/Abstractions/Grains/IScenarioRunnerGrain.cs
@@ -2,6 +2,7 @@ using Managing.Common;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Indicators;
+using Managing.Domain.Shared.Helpers;
namespace Managing.Application.Abstractions.Grains;
@@ -17,8 +18,11 @@ public interface IScenarioRunnerGrain : IGrainWithGuidKey
/// The trading bot configuration
/// Previous signals to consider
/// Trading Exchange
+ /// The last candle
+ /// Optional indicator combo configuration (for user settings)
/// The generated signal or null if no signal
Task GetSignals(TradingBotConfig config, Dictionary previousSignals,
Enums.TradingExchanges tradingExchange,
- Candle lastCandle);
+ Candle lastCandle,
+ IndicatorComboConfig indicatorComboConfig = null);
}
\ No newline at end of file
diff --git a/src/Managing.Application/Bots/FuturesBot.cs b/src/Managing.Application/Bots/FuturesBot.cs
index eb815702..caaa8a9a 100644
--- a/src/Managing.Application/Bots/FuturesBot.cs
+++ b/src/Managing.Application/Bots/FuturesBot.cs
@@ -714,7 +714,11 @@ public class FuturesBot : TradingBotBase
await ServiceScopeHelpers.WithScopedService(_scopeFactory, async grainFactory =>
{
var scenarioRunnerGrain = grainFactory.GetGrain(Guid.NewGuid());
- var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle);
+
+ // Create indicator combo config from user settings
+ var indicatorComboConfig = TradingBox.CreateConfigFromUserSettings(Account.User);
+
+ var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle, indicatorComboConfig);
if (signal == null) return;
await AddSignal(signal);
});
diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs
index 76cb0c68..83869f51 100644
--- a/src/Managing.Application/Bots/Grains/AgentGrain.cs
+++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs
@@ -313,6 +313,10 @@ public class AgentGrain : Grain, IAgentGrain
public async Task CheckAndEnsureEthBalanceAsync(Guid requestingBotId, string accountName)
{
+ // Get user settings
+ var userId = (int)this.GetPrimaryKeyLong();
+ var user = await _userService.GetUserByIdAsync(userId);
+
// Check if a swap is already in progress
if (_state.State.IsSwapInProgress)
{
@@ -358,6 +362,15 @@ public class AgentGrain : Grain, IAgentGrain
};
}
+ // Check low ETH amount alert threshold
+ var lowEthAlertThreshold = user.LowEthAmountAlert ?? Constants.GMX.Config.MinimumTradeEthBalanceUsd;
+ if (balanceData.EthValueInUsd < lowEthAlertThreshold)
+ {
+ _logger.LogWarning(
+ "ETH balance below alert threshold for user {UserId} - ETH: {EthValue:F2} USD (threshold: {Threshold:F2} USD)",
+ userId, balanceData.EthValueInUsd, lowEthAlertThreshold);
+ }
+
_logger.LogInformation(
"Agent {UserId} balance check - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (cached: {IsCached})",
this.GetPrimaryKeyLong(), balanceData.EthValueInUsd, balanceData.UsdcValue,
@@ -402,18 +415,34 @@ public class AgentGrain : Grain, IAgentGrain
};
}
- // Check if we have enough USDC for swap (need at least 5 USD for swap)
- if (balanceData.UsdcValue <
- (Constants.GMX.Config.MinimumPositionAmount + (decimal)Constants.GMX.Config.AutoSwapAmount))
+ // Check if autoswap is enabled for this user
+ if (!user.EnableAutoswap)
+ {
+ _logger.LogInformation("Autoswap is disabled for user {UserId}, skipping swap",
+ userId);
+ return new BalanceCheckResult
+ {
+ IsSuccessful = false,
+ FailureReason = BalanceCheckFailureReason.None,
+ Message = "Autoswap is disabled for this user",
+ ShouldStopBot = false
+ };
+ }
+
+ // Get autoswap amount from user settings or use default
+ var autoswapAmount = user.AutoswapAmount ?? (decimal)Constants.GMX.Config.AutoSwapAmount;
+
+ // Check if we have enough USDC for swap
+ if (balanceData.UsdcValue < (Constants.GMX.Config.MinimumPositionAmount + autoswapAmount))
{
_logger.LogWarning(
"Insufficient USDC balance for swap - ETH: {EthValue:F2} USD, USDC: {UsdcValue:F2} USD (need {AutoSwapAmount} USD for swap)",
- balanceData.EthValueInUsd, balanceData.UsdcValue, Constants.GMX.Config.AutoSwapAmount);
+ balanceData.EthValueInUsd, balanceData.UsdcValue, autoswapAmount);
return new BalanceCheckResult
{
IsSuccessful = false,
FailureReason = BalanceCheckFailureReason.InsufficientUsdcForSwap,
- Message = $"Insufficient USDC balance for swap (need {Constants.GMX.Config.AutoSwapAmount} USD)",
+ Message = $"Insufficient USDC balance for swap (need {autoswapAmount} USD)",
ShouldStopBot = true
};
}
@@ -440,22 +469,18 @@ public class AgentGrain : Grain, IAgentGrain
try
{
- _logger.LogInformation("Initiating USDC to ETH swap for agent {UserId} - swapping 5 USDC",
- this.GetPrimaryKeyLong());
+ _logger.LogInformation("Initiating USDC to ETH swap for agent {UserId} - swapping {Amount} USDC",
+ this.GetPrimaryKeyLong(), autoswapAmount);
- // Get user for the swap
- var userId = (int)this.GetPrimaryKeyLong();
- var user = await _userService.GetUserByIdAsync(userId);
-
- // Perform the swap
+ // Perform the swap using user's autoswap amount
var swapInfo = await _tradingService.SwapGmxTokensAsync(user, accountName,
- Ticker.USDC, Ticker.ETH, Constants.GMX.Config.AutoSwapAmount);
+ Ticker.USDC, Ticker.ETH, (double)autoswapAmount);
if (swapInfo.Success)
{
_logger.LogInformation(
- "Successfully swapped 5 USDC to ETH for agent {UserId}, transaction hash: {Hash}",
- userId, swapInfo.Hash);
+ "Successfully swapped {Amount} USDC to ETH for agent {UserId}, transaction hash: {Hash}",
+ autoswapAmount, userId, swapInfo.Hash);
// Update last swap time and invalidate cache
_state.State.LastSwapTime = DateTime.UtcNow;
diff --git a/src/Managing.Application/Bots/SpotBot.cs b/src/Managing.Application/Bots/SpotBot.cs
index 2b92ee0b..995be313 100644
--- a/src/Managing.Application/Bots/SpotBot.cs
+++ b/src/Managing.Application/Bots/SpotBot.cs
@@ -860,7 +860,11 @@ public class SpotBot : TradingBotBase
await ServiceScopeHelpers.WithScopedService(_scopeFactory, async grainFactory =>
{
var scenarioRunnerGrain = grainFactory.GetGrain(Guid.NewGuid());
- var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle);
+
+ // Create indicator combo config from user settings
+ var indicatorComboConfig = TradingBox.CreateConfigFromUserSettings(Account.User);
+
+ var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, Account.Exchange, LastCandle, indicatorComboConfig);
if (signal == null) return;
await AddSignal(signal);
});
diff --git a/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs b/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs
index b92723a9..62f26226 100644
--- a/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs
+++ b/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs
@@ -56,7 +56,7 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain
}
public async Task GetSignals(TradingBotConfig config, Dictionary previousSignals,
- TradingExchanges tradingExchanges, Candle candle)
+ TradingExchanges tradingExchanges, Candle candle, IndicatorComboConfig indicatorComboConfig = null)
{
try
{
@@ -84,11 +84,16 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain
_logger.LogInformation($"Fetched {candlesList.Count} candles for {config.Ticker} for {config.Name}");
+ // Use provided config or default
+ var comboConfig = indicatorComboConfig ?? new IndicatorComboConfig();
+
var signal = TradingBox.GetSignal(
candlesList,
config.Scenario,
previousSignals,
- config.Scenario?.LookbackPeriod ?? 1);
+ comboConfig,
+ config.Scenario?.LookbackPeriod ?? 1,
+ null);
if (signal != null && signal.Date > candle.Date)
{
diff --git a/src/Managing.Application/Users/UserService.cs b/src/Managing.Application/Users/UserService.cs
index ba5261d5..d870b090 100644
--- a/src/Managing.Application/Users/UserService.cs
+++ b/src/Managing.Application/Users/UserService.cs
@@ -365,6 +365,18 @@ public class UserService : IUserService
if (settings.MinimumConfidence.HasValue)
user.MinimumConfidence = settings.MinimumConfidence.Value;
+ if (settings.TrendStrongAgreementThreshold.HasValue)
+ user.TrendStrongAgreementThreshold = settings.TrendStrongAgreementThreshold.Value;
+
+ if (settings.SignalAgreementThreshold.HasValue)
+ user.SignalAgreementThreshold = settings.SignalAgreementThreshold.Value;
+
+ if (settings.AllowSignalTrendOverride.HasValue)
+ user.AllowSignalTrendOverride = settings.AllowSignalTrendOverride.Value;
+
+ if (settings.DefaultExchange.HasValue)
+ user.DefaultExchange = settings.DefaultExchange.Value;
+
await _userRepository.SaveOrUpdateUserAsync(user);
return user;
}
diff --git a/src/Managing.Domain/Shared/Helpers/TradingBox.cs b/src/Managing.Domain/Shared/Helpers/TradingBox.cs
index 6d5b05cc..66a00998 100644
--- a/src/Managing.Domain/Shared/Helpers/TradingBox.cs
+++ b/src/Managing.Domain/Shared/Helpers/TradingBox.cs
@@ -9,6 +9,8 @@ using Managing.Domain.Statistics;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Trades;
+using Managing.Domain.Users;
+using Orleans;
using static Managing.Common.Enums;
namespace Managing.Domain.Shared.Helpers;
@@ -16,45 +18,72 @@ namespace Managing.Domain.Shared.Helpers;
///
/// Configuration for strategy combination logic
///
+[GenerateSerializer]
public class IndicatorComboConfig
{
///
/// Minimum percentage of trend strategies that must agree for strong trend (default: 66%)
///
+ [Id(0)]
public decimal TrendStrongAgreementThreshold { get; set; } = 0.66m;
///
/// Minimum percentage of signal strategies that must agree (default: 50%)
///
+ [Id(1)]
public decimal SignalAgreementThreshold { get; set; } = 0.5m;
///
/// Whether to allow signal strategies to override conflicting trends (default: true)
/// This is useful for trend reversal signals
///
+ [Id(2)]
public bool AllowSignalTrendOverride { get; set; } = true;
///
/// Minimum confidence level to return a signal (default: Low)
///
+ [Id(3)]
public Confidence MinimumConfidence { get; set; } = Confidence.Low;
///
/// Minimum confidence level required from context strategies (default: Medium)
/// Context strategies evaluate market conditions - higher requirements mean more conservative trading
///
+ [Id(4)]
public Confidence MinimumContextConfidence { get; set; } = Confidence.Medium;
///
- /// Default exchange to use when signals don't specify one
+ /// Default exchange to use when signals don't specify one (defaults to GMX)
///
- public TradingExchanges DefaultExchange { get; set; } = TradingExchanges.Binance;
+ [Id(5)]
+ public TradingExchanges DefaultExchange { get; set; } = TradingExchanges.GmxV2;
}
public static class TradingBox
{
private static readonly IndicatorComboConfig _defaultConfig = new();
+ ///
+ /// Creates an IndicatorComboConfig from user settings, using defaults for any null values
+ ///
+ /// The user with settings to apply
+ /// IndicatorComboConfig with user settings applied
+ public static IndicatorComboConfig CreateConfigFromUserSettings(User user)
+ {
+ var config = new IndicatorComboConfig();
+
+ // Apply user's indicator combo settings, using defaults if not set
+ config.MinimumContextConfidence = user.MinimumConfidence ?? Confidence.Medium;
+ config.TrendStrongAgreementThreshold = user.TrendStrongAgreementThreshold ?? 0.66m;
+ config.SignalAgreementThreshold = user.SignalAgreementThreshold ?? 0.5m;
+ config.AllowSignalTrendOverride = user.AllowSignalTrendOverride ?? true;
+ // DefaultExchange defaults to GMX if not set
+ config.DefaultExchange = user.DefaultExchange ?? TradingExchanges.GmxV2;
+
+ return config;
+ }
+
public static LightSignal GetSignal(IReadOnlyList newCandles, LightScenario scenario,
Dictionary previousSignal, int? loopbackPeriod = 1)
{
diff --git a/src/Managing.Domain/Users/User.cs b/src/Managing.Domain/Users/User.cs
index b93706f5..43c2897e 100644
--- a/src/Managing.Domain/Users/User.cs
+++ b/src/Managing.Domain/Users/User.cs
@@ -25,12 +25,18 @@ public class User
[Id(8)] public DateTimeOffset? LastConnectionDate { get; set; }
- // User Settings
+ // User Settings - Trading Configuration
[Id(9)] public decimal? LowEthAmountAlert { get; set; }
[Id(10)] public bool EnableAutoswap { get; set; } = false;
[Id(11)] public decimal? AutoswapAmount { get; set; }
[Id(12)] public int? MaxWaitingTimeForPositionToGetFilledSeconds { get; set; }
[Id(13)] public decimal? MaxTxnGasFeePerPosition { get; set; }
[Id(14)] public bool IsGmxEnabled { get; set; } = false;
+
+ // User Settings - Indicator Combo Configuration
[Id(15)] public Confidence? MinimumConfidence { get; set; }
+ [Id(16)] public decimal? TrendStrongAgreementThreshold { get; set; }
+ [Id(17)] public decimal? SignalAgreementThreshold { get; set; }
+ [Id(18)] public bool? AllowSignalTrendOverride { get; set; }
+ [Id(19)] public TradingExchanges? DefaultExchange { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Database/Migrations/20251229224000_AddUserSettings.cs b/src/Managing.Infrastructure.Database/Migrations/20251229224000_AddUserSettings.cs
index 2a8d1e35..66c46a4c 100644
--- a/src/Managing.Infrastructure.Database/Migrations/20251229224000_AddUserSettings.cs
+++ b/src/Managing.Infrastructure.Database/Migrations/20251229224000_AddUserSettings.cs
@@ -14,7 +14,8 @@ namespace Managing.Infrastructure.Databases.Migrations
name: "AutoswapAmount",
table: "Users",
type: "numeric(18,8)",
- nullable: true);
+ nullable: true,
+ defaultValue: 3m);
migrationBuilder.AddColumn(
name: "EnableAutoswap",
@@ -34,25 +35,29 @@ namespace Managing.Infrastructure.Databases.Migrations
name: "LowEthAmountAlert",
table: "Users",
type: "numeric(18,8)",
- nullable: true);
+ nullable: true,
+ defaultValue: 1.5m);
migrationBuilder.AddColumn(
name: "MaxTxnGasFeePerPosition",
table: "Users",
type: "numeric(18,8)",
- nullable: true);
+ nullable: true,
+ defaultValue: 1.5m);
migrationBuilder.AddColumn(
name: "MaxWaitingTimeForPositionToGetFilledSeconds",
table: "Users",
type: "integer",
- nullable: true);
+ nullable: true,
+ defaultValue: 600);
migrationBuilder.AddColumn(
name: "MinimumConfidence",
table: "Users",
type: "text",
- nullable: true);
+ nullable: true,
+ defaultValue: "Medium");
}
///
diff --git a/src/Managing.Infrastructure.Database/Migrations/20251229225934_SetUserSettingsDefaults.Designer.cs b/src/Managing.Infrastructure.Database/Migrations/20251229225934_SetUserSettingsDefaults.Designer.cs
new file mode 100644
index 00000000..237624fd
--- /dev/null
+++ b/src/Managing.Infrastructure.Database/Migrations/20251229225934_SetUserSettingsDefaults.Designer.cs
@@ -0,0 +1,1773 @@
+//
+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("20251229225934_SetUserSettingsDefaults")]
+ partial class SetUserSettingsDefaults
+ {
+ ///
+ 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