diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs
index 25f8c7d5..232c9124 100644
--- a/src/Managing.Api/Controllers/DataController.cs
+++ b/src/Managing.Api/Controllers/DataController.cs
@@ -498,7 +498,8 @@ public class DataController : ControllerBase
PnL = strategy.Pnl,
NetPnL = strategy.NetPnL,
ROIPercentage = strategy.Roi,
- Runtime = strategy.StartupTime,
+ Runtime = strategy.Status == BotStatus.Running ? strategy.LastStartTime : null,
+ TotalRuntimeSeconds = strategy.GetTotalRuntimeSeconds(),
WinRate = winRate,
TotalVolumeTraded = totalVolume,
VolumeLast24H = volumeLast24h,
diff --git a/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs b/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs
index 7e5affc0..922c5561 100644
--- a/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs
+++ b/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs
@@ -33,9 +33,14 @@ namespace Managing.Api.Models.Responses
public decimal ROIPercentage { get; set; }
///
- /// Date and time when the strategy was started
+ /// Date and time when the strategy was started (only present when running, for live ticker)
///
- public DateTime Runtime { get; set; }
+ public DateTime? Runtime { get; set; }
+
+ ///
+ /// Total accumulated runtime in seconds (including current session if running)
+ ///
+ public long TotalRuntimeSeconds { get; set; }
///
/// Average percentage of successful trades
diff --git a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs
index 4e77951a..bebb0976 100644
--- a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs
+++ b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs
@@ -170,6 +170,10 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
// Set startup time when bot actually starts running
_state.State.StartupTime = DateTime.UtcNow;
+
+ // Track runtime: set LastStartTime when bot starts
+ _state.State.LastStartTime = DateTime.UtcNow;
+
await _state.WriteStateAsync();
// Start the in-memory timer and persistent reminder
@@ -281,6 +285,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
StopAndDisposeTimer();
await UnregisterReminder();
+ // Track runtime: accumulate current session runtime when stopping
+ if (_state.State.LastStartTime.HasValue)
+ {
+ var currentSessionSeconds = (long)(DateTime.UtcNow - _state.State.LastStartTime.Value).TotalSeconds;
+ _state.State.AccumulatedRunTimeSeconds += currentSessionSeconds;
+ _state.State.LastStopTime = DateTime.UtcNow;
+ _state.State.LastStartTime = null; // Clear since bot is no longer running
+
+ _logger.LogInformation("Bot {GrainId} accumulated {Seconds} seconds of runtime. Total: {TotalSeconds} seconds",
+ this.GetPrimaryKey(), currentSessionSeconds, _state.State.AccumulatedRunTimeSeconds);
+ }
+
// Sync state from the volatile TradingBotBase before destroying it
SyncStateFromBase();
await _state.WriteStateAsync();
diff --git a/src/Managing.Application/Bots/TradingBotGrainState.cs b/src/Managing.Application/Bots/TradingBotGrainState.cs
index 84948a9d..33e16cdf 100644
--- a/src/Managing.Application/Bots/TradingBotGrainState.cs
+++ b/src/Managing.Application/Bots/TradingBotGrainState.cs
@@ -121,4 +121,22 @@ public class TradingBotGrainState
///
[Id(18)]
public Candle? LastCandle { get; set; }
+
+ ///
+ /// The last time the bot was started (for runtime tracking)
+ ///
+ [Id(19)]
+ public DateTime? LastStartTime { get; set; }
+
+ ///
+ /// The last time the bot was stopped (for runtime tracking)
+ ///
+ [Id(20)]
+ public DateTime? LastStopTime { get; set; }
+
+ ///
+ /// Total accumulated runtime in seconds (excluding current session if running)
+ ///
+ [Id(21)]
+ public long AccumulatedRunTimeSeconds { get; set; }
}
\ No newline at end of file
diff --git a/src/Managing.Domain/Bots/Bot.cs b/src/Managing.Domain/Bots/Bot.cs
index 615ee2e6..8a9fc039 100644
--- a/src/Managing.Domain/Bots/Bot.cs
+++ b/src/Managing.Domain/Bots/Bot.cs
@@ -13,6 +13,11 @@ namespace Managing.Domain.Bots
public DateTime StartupTime { get; set; }
public DateTime CreateDate { get; set; }
+ // Runtime tracking fields
+ public DateTime? LastStartTime { get; set; }
+ public DateTime? LastStopTime { get; set; }
+ public long AccumulatedRunTimeSeconds { get; set; }
+
public int TradeWins { get; set; }
public int TradeLosses { get; set; }
public decimal Pnl { get; set; }
@@ -23,5 +28,21 @@ namespace Managing.Domain.Bots
public int LongPositionCount { get; set; }
public int ShortPositionCount { get; set; }
+ ///
+ /// Gets the total runtime in seconds, including the current session if the bot is running
+ ///
+ public long GetTotalRuntimeSeconds()
+ {
+ var totalSeconds = AccumulatedRunTimeSeconds;
+
+ // If bot is currently running, add current session time
+ if (Status == BotStatus.Running && LastStartTime.HasValue)
+ {
+ var currentSessionSeconds = (long)(DateTime.UtcNow - LastStartTime.Value).TotalSeconds;
+ totalSeconds += currentSessionSeconds;
+ }
+
+ return totalSeconds;
+ }
}
}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Database/Migrations/20251005132435_AddBotRuntimeTracking.Designer.cs b/src/Managing.Infrastructure.Database/Migrations/20251005132435_AddBotRuntimeTracking.Designer.cs
new file mode 100644
index 00000000..4b9a835c
--- /dev/null
+++ b/src/Managing.Infrastructure.Database/Migrations/20251005132435_AddBotRuntimeTracking.Designer.cs
@@ -0,0 +1,1467 @@
+//
+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("20251005132435_AddBotRuntimeTracking")]
+ partial class AddBotRuntimeTracking
+ {
+ ///
+ 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("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")
+ .HasMaxLength(255)
+ .HasColumnType("uuid");
+
+ 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("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("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")
+ .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")
+ .HasMaxLength(255)
+ .HasColumnType("uuid");
+
+ 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("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("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