diff --git a/src/Managing.Api/Controllers/BacktestController.cs b/src/Managing.Api/Controllers/BacktestController.cs index bf797623..9788c5ec 100644 --- a/src/Managing.Api/Controllers/BacktestController.cs +++ b/src/Managing.Api/Controllers/BacktestController.cs @@ -301,7 +301,9 @@ public class BacktestController : BaseController Fees = b.Fees, SharpeRatio = b.SharpeRatio, Score = b.Score, - ScoreMessage = b.ScoreMessage + ScoreMessage = b.ScoreMessage, + InitialBalance = b.InitialBalance, + NetPnl = b.NetPnl }), TotalCount = totalCount, CurrentPage = page, @@ -435,7 +437,9 @@ public class BacktestController : BaseController Fees = b.Fees, SharpeRatio = b.SharpeRatio, Score = b.Score, - ScoreMessage = b.ScoreMessage + ScoreMessage = b.ScoreMessage, + InitialBalance = b.InitialBalance, + NetPnl = b.NetPnl }), TotalCount = totalCount, CurrentPage = page, diff --git a/src/Managing.Api/Models/Requests/LightBacktestResponse.cs b/src/Managing.Api/Models/Requests/LightBacktestResponse.cs index 1f3d1269..754cd4fa 100644 --- a/src/Managing.Api/Models/Requests/LightBacktestResponse.cs +++ b/src/Managing.Api/Models/Requests/LightBacktestResponse.cs @@ -20,6 +20,8 @@ public class LightBacktestResponse [Required] public double? SharpeRatio { get; set; } [Required] public double Score { get; set; } [Required] public string ScoreMessage { get; set; } = string.Empty; + [Required] public decimal InitialBalance { get; set; } + [Required] public decimal NetPnl { get; set; } } public static class LightBacktestResponseMapper @@ -41,7 +43,9 @@ public static class LightBacktestResponseMapper Fees = b.Fees, SharpeRatio = (double?)b.Statistics?.SharpeRatio, Score = b.Score, - ScoreMessage = b.ScoreMessage + ScoreMessage = b.ScoreMessage, + InitialBalance = b.InitialBalance, + NetPnl = b.NetPnl }; } } \ No newline at end of file diff --git a/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs index 20367cd5..b4ebfb88 100644 --- a/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs +++ b/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs @@ -166,6 +166,8 @@ public class BacktestTradingBotGrain : Grain, IBacktestTradingBotGrain Metadata = metadata, StartDate = candles.FirstOrDefault()!.OpenTime, EndDate = candles.LastOrDefault()!.OpenTime, + InitialBalance = tradingBot.WalletBalances.FirstOrDefault().Value, + NetPnl = finalPnl - fees, }; if (save && user != null) @@ -201,7 +203,9 @@ public class BacktestTradingBotGrain : Grain, IBacktestTradingBotGrain Fees = backtest.Fees, SharpeRatio = (double?)backtest.Statistics?.SharpeRatio, Score = backtest.Score, - ScoreMessage = backtest.ScoreMessage + ScoreMessage = backtest.ScoreMessage, + InitialBalance = backtest.InitialBalance, + NetPnl = backtest.NetPnl }; } diff --git a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs index 46cd3b82..938bbe7e 100644 --- a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs +++ b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs @@ -172,7 +172,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable // Fallback: Get the first account for the user if (_state.State.User == null) { - throw new InvalidOperationException($"Bot '{config.Name}' (ID: {_state.State.Identifier}) has no user information. Cannot determine fallback account."); + throw new InvalidOperationException( + $"Bot '{config.Name}' (ID: {_state.State.Identifier}) has no user information. Cannot determine fallback account."); } var firstAccount = await ServiceScopeHelpers.WithScopedService( @@ -183,8 +184,10 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable var account = userAccounts.FirstOrDefault(); if (account == null) { - throw new InvalidOperationException($"User '{_state.State.User.Name}' has no accounts configured."); + throw new InvalidOperationException( + $"User '{_state.State.User.Name}' has no accounts configured."); } + return account; }); @@ -193,7 +196,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable _state.State.Config = config; await _state.WriteStateAsync(); - _logger.LogInformation("Bot '{BotName}' (ID: {BotId}) using fallback account '{AccountName}' for user '{UserName}'", + _logger.LogInformation( + "Bot '{BotName}' (ID: {BotId}) using fallback account '{AccountName}' for user '{UserName}'", config.Name, _state.State.Identifier, firstAccount.Name, _state.State.User.Name); } @@ -461,6 +465,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable { _logger.LogError(ex, "Error during coordinated balance check for bot {BotId}", _state.State.Identifier); + SentrySdk.CaptureException(ex); // Continue execution to avoid stopping the bot due to coordination errors } } @@ -1046,7 +1051,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable { if (_state.State.User == null) { - throw new InvalidOperationException($"Bot '{_state.State.Config?.Name}' (ID: {_state.State.Identifier}) has no user information."); + throw new InvalidOperationException( + $"Bot '{_state.State.Config?.Name}' (ID: {_state.State.Identifier}) has no user information."); } return Task.FromResult(_state.State.User); diff --git a/src/Managing.Domain/Backtests/Backtest.cs b/src/Managing.Domain/Backtests/Backtest.cs index ec2d34ce..dcd6b094 100644 --- a/src/Managing.Domain/Backtests/Backtest.cs +++ b/src/Managing.Domain/Backtests/Backtest.cs @@ -55,6 +55,8 @@ public class Backtest public Guid RequestId { get; set; } public object? Metadata { get; set; } public string ScoreMessage { get; set; } = string.Empty; + [Required] public decimal InitialBalance { get; set; } + [Required] public decimal NetPnl { get; set; } /// /// Creates a new TradingBotConfig based on this backtest's configuration for starting a live bot. diff --git a/src/Managing.Domain/Backtests/LightBacktest.cs b/src/Managing.Domain/Backtests/LightBacktest.cs index 997403e3..8069f887 100644 --- a/src/Managing.Domain/Backtests/LightBacktest.cs +++ b/src/Managing.Domain/Backtests/LightBacktest.cs @@ -25,4 +25,6 @@ public class LightBacktest [Id(12)] public string ScoreMessage { get; set; } = string.Empty; [Id(13)] public object Metadata { get; set; } [Id(14)] public string Ticker { get; set; } = string.Empty; + [Id(15)] public decimal InitialBalance { get; set; } + [Id(16)] public decimal NetPnl { get; set; } } \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs b/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs index e6f46757..fa8f7ff9 100644 --- a/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs +++ b/src/Managing.Infrastructure.Database/Migrations/ManagingDbContextModelSnapshot.cs @@ -189,6 +189,9 @@ namespace Managing.Infrastructure.Databases.Migrations .IsRequired() .HasColumnType("text"); + b.Property("InitialBalance") + .HasColumnType("decimal(18,8)"); + b.Property("MaxDrawdown") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,8)") @@ -211,6 +214,9 @@ namespace Managing.Infrastructure.Databases.Migrations .HasMaxLength(255) .HasColumnType("character varying(255)"); + b.Property("NetPnl") + .HasColumnType("decimal(18,8)"); + b.Property("PositionsJson") .IsRequired() .HasColumnType("jsonb"); diff --git a/src/Managing.Infrastructure.Database/PostgreSql/Entities/BacktestEntity.cs b/src/Managing.Infrastructure.Database/PostgreSql/Entities/BacktestEntity.cs index c7a1125d..f4ce9867 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/Entities/BacktestEntity.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/Entities/BacktestEntity.cs @@ -120,4 +120,12 @@ public class BacktestEntity [Required] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; + + [Required] + [Column(TypeName = "decimal(18,8)")] + public decimal InitialBalance { get; set; } + + [Required] + [Column(TypeName = "decimal(18,8)")] + public decimal NetPnl { get; set; } } \ No newline at end of file diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs index ea2ae5a5..807950a4 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs @@ -313,7 +313,9 @@ public static class PostgreSqlMappers Score = entity.Score, ScoreMessage = entity.ScoreMessage, RequestId = entity.RequestId, - Metadata = entity.Metadata + Metadata = entity.Metadata, + InitialBalance = entity.InitialBalance, + NetPnl = entity.NetPnl }; return backtest; @@ -353,7 +355,9 @@ public static class PostgreSqlMappers ScoreMessage = backtest.ScoreMessage ?? string.Empty, Metadata = backtest.Metadata?.ToString(), CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow + UpdatedAt = DateTime.UtcNow, + InitialBalance = backtest.InitialBalance, + NetPnl = backtest.NetPnl }; }