diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs
index 3682e95..1efe2a1 100644
--- a/src/Managing.Api/Controllers/DataController.cs
+++ b/src/Managing.Api/Controllers/DataController.cs
@@ -12,7 +12,6 @@ using Managing.Domain.Scenarios;
using Managing.Domain.Statistics;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
-using Managing.Domain.Trades;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -411,9 +410,10 @@ public class DataController : ControllerBase
var userStrategies = await _mediator.Send(new GetUserStrategiesCommand(agentName));
// Convert to detailed view model with additional information
- var result = userStrategies.Select(strategy => MapStrategyToViewModel(strategy)).ToList();
+ var tasks = userStrategies.Select(strategy => MapStrategyToViewModelAsync(strategy));
+ var result = await Task.WhenAll(tasks);
- return Ok(result);
+ return Ok(result.ToList());
}
///
@@ -434,7 +434,7 @@ public class DataController : ControllerBase
}
// Map the strategy to a view model using the shared method
- var result = MapStrategyToViewModel(strategy);
+ var result = await MapStrategyToViewModelAsync(strategy);
return Ok(result);
}
@@ -444,7 +444,7 @@ public class DataController : ControllerBase
///
/// The trading bot to map
/// A view model with detailed strategy information
- private UserStrategyDetailsViewModel MapStrategyToViewModel(Bot strategy)
+ private async Task MapStrategyToViewModelAsync(Bot strategy)
{
// Calculate ROI percentage based on PnL relative to account value
decimal pnl = strategy.Pnl;
@@ -464,6 +464,9 @@ public class DataController : ControllerBase
// Calculate ROI for last 24h
decimal roiLast24h = strategy.Roi;
+ // Fetch positions associated with this bot
+ var positions = await _tradingService.GetPositionsByInitiatorIdentifierAsync(strategy.Identifier);
+
return new UserStrategyDetailsViewModel
{
Name = strategy.Name,
@@ -477,7 +480,7 @@ public class DataController : ControllerBase
VolumeLast24H = volumeLast24h,
Wins = wins,
Losses = losses,
- Positions = new List(),
+ Positions = positions.ToList(),
Identifier = strategy.Identifier,
WalletBalances = new Dictionary(),
};
diff --git a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs
index c4192f7..8ba0e92 100644
--- a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs
+++ b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs
@@ -25,6 +25,7 @@ public interface ITradingRepository
Task GetPositionByIdentifierAsync(Guid identifier);
Task> GetPositionsAsync(PositionInitiator positionInitiator);
Task> GetPositionsByStatusAsync(PositionStatus positionStatus);
+ Task> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier);
Task> GetAllPositionsAsync();
Task UpdateScenarioAsync(Scenario scenario);
diff --git a/src/Managing.Application.Abstractions/Services/ITradingService.cs b/src/Managing.Application.Abstractions/Services/ITradingService.cs
index 3071461..bc40927 100644
--- a/src/Managing.Application.Abstractions/Services/ITradingService.cs
+++ b/src/Managing.Application.Abstractions/Services/ITradingService.cs
@@ -36,6 +36,7 @@ public interface ITradingService
Task UpdateIndicatorAsync(IndicatorBase indicatorBase);
Task> GetBrokerPositions(Account account);
Task> GetAllDatabasePositionsAsync();
+ Task> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier);
Task InitPrivyWallet(string publicAddress);
// Synth API integration methods
diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs
index 3e23a99..a2915ee 100644
--- a/src/Managing.Application/Bots/TradingBotBase.cs
+++ b/src/Managing.Application/Bots/TradingBotBase.cs
@@ -713,7 +713,8 @@ public class TradingBotBase : ITradingBot
Config.BotTradingBalance,
Config.IsForBacktest,
lastPrice,
- signalIdentifier: signal.Identifier);
+ signalIdentifier: signal.Identifier,
+ initiatorIdentifier: Identifier);
var position = await ServiceScopeHelpers
.WithScopedServices(
@@ -978,7 +979,7 @@ public class TradingBotBase : ITradingBot
_scopeFactory, async (exchangeService, accountService, tradingService) =>
{
closedPosition =
- await new ClosePositionCommandHandler(exchangeService, accountService, tradingService)
+ await new ClosePositionCommandHandler(exchangeService, accountService, tradingService, _scopeFactory)
.Handle(command);
});
diff --git a/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs b/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs
index 1e418e4..77f45ed 100644
--- a/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs
+++ b/src/Managing.Application/Trading/Commands/OpenPositionRequest.cs
@@ -18,7 +18,8 @@ namespace Managing.Application.Trading.Commands
decimal amountToTrade,
bool isForPaperTrading = false,
decimal? price = null,
- string signalIdentifier = null)
+ string signalIdentifier = null,
+ Guid? initiatorIdentifier = null)
{
AccountName = accountName;
MoneyManagement = moneyManagement;
@@ -38,6 +39,7 @@ namespace Managing.Application.Trading.Commands
IsForPaperTrading = isForPaperTrading;
Price = price;
SignalIdentifier = signalIdentifier;
+ InitiatorIdentifier = initiatorIdentifier ?? throw new ArgumentNullException(nameof(initiatorIdentifier), "InitiatorIdentifier is required");
}
public string SignalIdentifier { get; set; }
@@ -51,5 +53,6 @@ namespace Managing.Application.Trading.Commands
public DateTime Date { get; }
public PositionInitiator Initiator { get; }
public User User { get; }
+ public Guid InitiatorIdentifier { get; }
}
}
\ No newline at end of file
diff --git a/src/Managing.Application/Trading/Handlers/ClosePositionCommandHandler.cs b/src/Managing.Application/Trading/Handlers/ClosePositionCommandHandler.cs
index fecec70..7252dde 100644
--- a/src/Managing.Application/Trading/Handlers/ClosePositionCommandHandler.cs
+++ b/src/Managing.Application/Trading/Handlers/ClosePositionCommandHandler.cs
@@ -2,8 +2,10 @@
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
+using Managing.Core;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Trades;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
@@ -13,7 +15,7 @@ public class ClosePositionCommandHandler(
IExchangeService exchangeService,
IAccountService accountService,
ITradingService tradingService,
- IGrainFactory? grainFactory = null,
+ IServiceScopeFactory scopeFactory,
ILogger logger = null)
: ICommandHandler
{
@@ -70,29 +72,35 @@ public class ClosePositionCommandHandler(
if (!request.IsForBacktest)
await tradingService.UpdatePositionAsync(request.Position);
-
+
// Notify platform summary about the closed position
try
{
- var platformGrain = grainFactory?.GetGrain("platform-summary");
- if (platformGrain != null)
+ await ServiceScopeHelpers.WithScopedService(scopeFactory, async grainFactory =>
{
- var positionClosedEvent = new PositionClosedEvent
+ var platformGrain = grainFactory.GetGrain("platform-summary");
+ if (platformGrain != null)
{
- PositionId = request.Position.Identifier,
- Ticker = request.Position.Ticker,
- RealizedPnL = request.Position.ProfitAndLoss?.Realized ?? 0,
- Volume = closedPosition.Quantity * lastPrice * request.Position.Open.Leverage,
- InitialVolume = request.Position.Open.Quantity * request.Position.Open.Price * request.Position.Open.Leverage
- };
-
- await platformGrain.OnPositionClosedAsync(positionClosedEvent);
- }
+ var positionClosedEvent = new PositionClosedEvent
+ {
+ PositionId = request.Position.Identifier,
+ Ticker = request.Position.Ticker,
+ RealizedPnL = request.Position.ProfitAndLoss?.Realized ?? 0,
+ Volume = closedPosition.Quantity * lastPrice * request.Position.Open.Leverage,
+ InitialVolume = request.Position.Open.Quantity * request.Position.Open.Price *
+ request.Position.Open.Leverage
+ };
+
+ await platformGrain.OnPositionClosedAsync(positionClosedEvent);
+ }
+ });
}
catch (Exception ex)
{
SentrySdk.CaptureException(ex);
- logger?.LogError(ex, "Failed to notify platform summary about position closure for position {PositionId}", request.Position.Identifier);
+ logger?.LogError(ex,
+ "Failed to notify platform summary about position closure for position {PositionId}",
+ request.Position.Identifier);
}
}
diff --git a/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs b/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs
index 2eb32fa..c0106c1 100644
--- a/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs
+++ b/src/Managing.Application/Trading/Handlers/OpenPositionCommandHandler.cs
@@ -32,6 +32,8 @@ namespace Managing.Application.Trading.Handlers
position.SignalIdentifier = request.SignalIdentifier;
}
+ position.InitiatorIdentifier = request.InitiatorIdentifier;
+
// Always use BotTradingBalance directly as the balance to risk
decimal balanceToRisk = request.AmountToTrade;
diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs
index 3c9678a..a11e42a 100644
--- a/src/Managing.Application/Trading/TradingService.cs
+++ b/src/Managing.Application/Trading/TradingService.cs
@@ -255,6 +255,11 @@ public class TradingService : ITradingService
return await _tradingRepository.GetAllPositionsAsync();
}
+ public async Task> GetPositionsByInitiatorIdentifierAsync(Guid initiatorIdentifier)
+ {
+ return await _tradingRepository.GetPositionsByInitiatorIdentifierAsync(initiatorIdentifier);
+ }
+
private async Task ManageTrader(TraderFollowup a, List tickers)
{
var shortAddress = a.Account.Address.Substring(0, 6);
diff --git a/src/Managing.Domain/Trades/Position.cs b/src/Managing.Domain/Trades/Position.cs
index 9c65883..637566c 100644
--- a/src/Managing.Domain/Trades/Position.cs
+++ b/src/Managing.Domain/Trades/Position.cs
@@ -66,6 +66,11 @@ namespace Managing.Domain.Trades
[Id(14)] [Required] public User User { get; set; }
+ ///
+ /// Identifier of the bot or entity that initiated this position
+ ///
+ [Id(15)] [Required] public Guid InitiatorIdentifier { get; set; }
+
public bool IsFinished()
{
return Status switch
diff --git a/src/Managing.Infrastructure.Database/Migrations/20250815011248_AddInitiatorIdentifierToPositions.Designer.cs b/src/Managing.Infrastructure.Database/Migrations/20250815011248_AddInitiatorIdentifierToPositions.Designer.cs
new file mode 100644
index 0000000..ef197bc
--- /dev/null
+++ b/src/Managing.Infrastructure.Database/Migrations/20250815011248_AddInitiatorIdentifierToPositions.Designer.cs
@@ -0,0 +1,1431 @@
+//
+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("20250815011248_AddInitiatorIdentifierToPositions")]
+ partial class AddInitiatorIdentifierToPositions
+ {
+ ///
+ 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("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("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Losses")
+ .HasColumnType("integer");
+
+ b.Property("Runtime")
+ .HasColumnType("timestamp with time zone");
+
+ 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");
+
+ 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("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("Metadata")
+ .HasColumnType("text");
+
+ b.Property("MoneyManagementJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("PositionsJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("RequestId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Score")
+ .HasColumnType("double precision");
+
+ b.Property("ScoreMessage")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("text");
+
+ b.Property("SignalsJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("StartDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("StatisticsJson")
+ .HasColumnType("jsonb");
+
+ 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", "Score");
+
+ b.ToTable("Backtests");
+ });
+
+ modelBuilder.Entity("Managing.Infrastructure.Databases.PostgreSql.Entities.BotEntity", b =>
+ {
+ b.Property("Identifier")
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(255)
+ .HasColumnType("uuid");
+
+ b.Property("CreateDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Fees")
+ .HasPrecision(18, 8)
+ .HasColumnType("numeric(18,8)");
+
+ b.Property("LongPositionCount")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ 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")
+ .HasColumnType("integer");
+
+ b.Property("TradeLosses")
+ .HasColumnType("integer");
+
+ b.Property("TradeWins")
+ .HasColumnType("integer");
+
+ 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("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("BacktestRequestsJson")
+ .IsRequired()
+ .HasColumnType("text");
+
+ 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("ErrorMessage")
+ .HasColumnType("text");
+
+ b.Property("EstimatedTimeRemainingSeconds")
+ .HasColumnType("integer");
+
+ b.Property("FailedBacktests")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ProgressInfo")
+ .HasColumnType("text");
+
+ b.Property("RequestId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ResultsJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TotalBacktests")
+ .HasColumnType("integer");
+
+ 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.HasIndex("UserId", "CreatedAt");
+
+ 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.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("AccountName")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Date")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Initiator")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("InitiatorIdentifier")
+ .HasColumnType("uuid");
+
+ b.Property("MoneyManagementJson")
+ .HasColumnType("text");
+
+ 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("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("Fee")
+ .HasColumnType("decimal(18,8)");
+
+ b.Property("Leverage")
+ .HasColumnType("decimal(18,8)");
+
+ b.Property