Excellent! Let me create a comprehensive summary of all the changes made:
## Summary I've successfully implemented all the requested features to add BacktestId support to the strategies/bot system: ### 1. **Added BacktestId Column to BotEntity** ✅ - **File**: `src/Managing.Infrastructure.Database/PostgreSql/Entities/BotEntity.cs:47` - Added nullable `int? BacktestId` property with documentation - This allows bots to reference the backtest they were created from ### 2. **Updated Bot Domain Model** ✅ - **File**: `src/Managing.Domain/Bots/Bot.cs:37` - Added `BacktestId` property to the domain model - Maintains consistency between entity and domain layers ### 3. **Updated TradingBotConfig and TradingBotConfigRequest** ✅ - **File**: `src/Managing.Domain/Bots/TradingBotConfig.cs:131` - Added `[Id(24)] public int? BacktestId` with Orleans serialization attribute - **File**: `src/Managing.Domain/Bots/TradingBotConfigRequest.cs:119` - Added `BacktestId` property to the request model - These changes ensure BacktestId flows through the entire bot creation pipeline ### 4. **Updated Data Mappers** ✅ - **File**: `src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs` - Updated `Map(Bot bot)` at line 833 to include BacktestId - Updated `Map(BotEntity entity)` at line 799 to include BacktestId - Ensures proper mapping between entity and domain models ### 5. **Updated LiveTradingBotGrain** ✅ - **File**: `src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs` - Added BacktestId to bot creation at line 1078 (for saved bots) - Added BacktestId to bot creation at line 1145 (for running bots) - The grain now properly persists BacktestId when saving bot statistics ### 6. **Created New Endpoint for Backtest Stats** ✅ - **File**: `src/Managing.Api/Controllers/BacktestController.cs:114` - **New Endpoint**: `GET /Backtest/{id}/stats` - Returns only statistical information without positions, signals, or candles: - Basic info: id, name, ticker, timeframe, tradingType, startDate, endDate - Performance: initialBalance, finalPnl, netPnl, growthPercentage, hodlPercentage, winRate - Risk metrics: sharpeRatio, maxDrawdown, maxDrawdownRecoveryTime - Other: fees, score, scoreMessage, positionCount ### 7. **Created Database Migration** ✅ - **Generated Migration**: `AddBacktestIdToBots` - The migration adds a nullable integer column `BacktestId` to the `Bots` table - Ready to be applied with `dotnet ef database update` ### 8. **Regenerated Frontend API Client** ✅ - Ran `dotnet build` in `src/Managing.Nswag` - The `ManagingApi.ts` file has been regenerated with: - `backtestId` field in bot-related DTOs - New `/Backtest/{id}/stats` endpoint ## How It Works ### Starting a Bot from a Backtest: 1. Frontend sends `StartBotRequest` with `TradingBotConfigRequest` containing `backtestId` 2. `BotController` validates and prepares the request 3. `StartBotCommandHandler` creates the bot configuration with BacktestId 4. `LiveTradingBotGrain.CreateAsync()` receives the config and saves it to state 5. When the bot is saved via `SaveBotAsync()`, BacktestId is persisted to the database 6. The Bot entity now has a reference to its originating backtest ### Retrieving Backtest Stats: 1. Frontend calls `GET /Backtest/{id}/stats` with the backtest ID 2. Backend retrieves the full backtest from the database 3. Returns only the statistical summary (without heavy data like positions/signals/candles) 4. Frontend can display backtest performance metrics when viewing a bot ## Database Schema ```sql ALTER TABLE "Bots" ADD COLUMN "BacktestId" integer NULL; ``` All changes follow the project's architecture patterns (Controller → Application → Repository) and maintain backward compatibility through nullable BacktestId fields.
This commit is contained in:
@@ -104,6 +104,52 @@ public class BacktestController : BaseController
|
|||||||
return Ok(backtest);
|
return Ok(backtest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves only the statistical information for a specific backtest by ID.
|
||||||
|
/// This endpoint returns only the performance metrics without positions, signals, or candles.
|
||||||
|
/// Useful for displaying backtest stats when starting a bot from a backtest.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The ID of the backtest to retrieve stats for.</param>
|
||||||
|
/// <returns>The backtest statistics without detailed position/signal data.</returns>
|
||||||
|
[HttpGet("{id}/stats")]
|
||||||
|
public async Task<ActionResult<object>> GetBacktestStats(int id)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
var backtest = await _backtester.GetBacktestByIdForUserAsync(user, id.ToString());
|
||||||
|
|
||||||
|
if (backtest == null)
|
||||||
|
{
|
||||||
|
return NotFound($"Backtest with ID {id} not found or doesn't belong to the current user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return only the statistical information
|
||||||
|
var stats = new
|
||||||
|
{
|
||||||
|
id = backtest.Id,
|
||||||
|
name = backtest.Config.Name,
|
||||||
|
ticker = backtest.Config.Ticker,
|
||||||
|
timeframe = backtest.Config.Timeframe,
|
||||||
|
tradingType = backtest.Config.TradingType,
|
||||||
|
startDate = backtest.StartDate,
|
||||||
|
endDate = backtest.EndDate,
|
||||||
|
initialBalance = backtest.InitialBalance,
|
||||||
|
finalPnl = backtest.FinalPnl,
|
||||||
|
netPnl = backtest.NetPnl,
|
||||||
|
growthPercentage = backtest.GrowthPercentage,
|
||||||
|
hodlPercentage = backtest.HodlPercentage,
|
||||||
|
winRate = backtest.WinRate,
|
||||||
|
sharpeRatio = backtest.Statistics?.SharpeRatio ?? 0,
|
||||||
|
maxDrawdown = backtest.Statistics?.MaxDrawdown ?? 0,
|
||||||
|
maxDrawdownRecoveryTime = backtest.Statistics?.MaxDrawdownRecoveryTime ?? TimeSpan.Zero,
|
||||||
|
fees = backtest.Fees,
|
||||||
|
score = backtest.Score,
|
||||||
|
scoreMessage = backtest.ScoreMessage,
|
||||||
|
positionCount = backtest.PositionCount
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(stats);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes a specific backtest by ID for the authenticated user.
|
/// Deletes a specific backtest by ID for the authenticated user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1075,6 +1075,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
|||||||
Volume = 0,
|
Volume = 0,
|
||||||
Fees = 0,
|
Fees = 0,
|
||||||
BotTradingBalance = _state.State.Config.BotTradingBalance,
|
BotTradingBalance = _state.State.Config.BotTradingBalance,
|
||||||
|
BacktestId = _state.State.Config.BacktestId,
|
||||||
MasterBotUserId = _state.State.Config.MasterBotUserId
|
MasterBotUserId = _state.State.Config.MasterBotUserId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1141,6 +1142,7 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
|||||||
LongPositionCount = longPositionCount,
|
LongPositionCount = longPositionCount,
|
||||||
ShortPositionCount = shortPositionCount,
|
ShortPositionCount = shortPositionCount,
|
||||||
BotTradingBalance = _state.State.Config.BotTradingBalance,
|
BotTradingBalance = _state.State.Config.BotTradingBalance,
|
||||||
|
BacktestId = _state.State.Config.BacktestId,
|
||||||
MasterBotUserId = _state.State.Config.MasterBotUserId
|
MasterBotUserId = _state.State.Config.MasterBotUserId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ namespace Managing.Domain.Bots
|
|||||||
|
|
||||||
public decimal BotTradingBalance { get; set; }
|
public decimal BotTradingBalance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The backtest ID associated with this bot (nullable for bots not created from backtests)
|
||||||
|
/// </summary>
|
||||||
|
public int? BacktestId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user ID of the master bot's owner when this bot is for copy trading
|
/// The user ID of the master bot's owner when this bot is for copy trading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -123,4 +123,10 @@ public class TradingBotConfig
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Id(23)]
|
[Id(23)]
|
||||||
public int? MasterBotUserId { get; set; }
|
public int? MasterBotUserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The backtest ID associated with this bot configuration (nullable for bots not created from backtests)
|
||||||
|
/// </summary>
|
||||||
|
[Id(24)]
|
||||||
|
public int? BacktestId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -112,4 +112,9 @@ public class TradingBotConfigRequest
|
|||||||
public bool UseForDynamicStopLoss { get; set; } = true;
|
public bool UseForDynamicStopLoss { get; set; } = true;
|
||||||
|
|
||||||
public TradingType TradingType { get; set; }
|
public TradingType TradingType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The backtest ID associated with this bot configuration (nullable for bots not created from backtests)
|
||||||
|
/// </summary>
|
||||||
|
public int? BacktestId { get; set; }
|
||||||
}
|
}
|
||||||
1799
src/Managing.Infrastructure.Database/Migrations/20260109112209_AddBacktestIdToBots.Designer.cs
generated
Normal file
1799
src/Managing.Infrastructure.Database/Migrations/20260109112209_AddBacktestIdToBots.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Managing.Infrastructure.Databases.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddBacktestIdToBots : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "BacktestId",
|
||||||
|
table: "Bots",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BacktestId",
|
||||||
|
table: "Bots");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -305,6 +305,9 @@ namespace Managing.Infrastructure.Databases.Migrations
|
|||||||
b.Property<long>("AccumulatedRunTimeSeconds")
|
b.Property<long>("AccumulatedRunTimeSeconds")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int?>("BacktestId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.Property<decimal>("BotTradingBalance")
|
b.Property<decimal>("BotTradingBalance")
|
||||||
.HasColumnType("numeric");
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ public class BotEntity
|
|||||||
|
|
||||||
public decimal BotTradingBalance { get; set; }
|
public decimal BotTradingBalance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The backtest ID associated with this bot (nullable for bots not created from backtests)
|
||||||
|
/// </summary>
|
||||||
|
public int? BacktestId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user ID of the master bot's owner when this bot is for copy trading
|
/// The user ID of the master bot's owner when this bot is for copy trading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -796,6 +796,7 @@ public static class PostgreSqlMappers
|
|||||||
LongPositionCount = entity.LongPositionCount,
|
LongPositionCount = entity.LongPositionCount,
|
||||||
ShortPositionCount = entity.ShortPositionCount,
|
ShortPositionCount = entity.ShortPositionCount,
|
||||||
BotTradingBalance = entity.BotTradingBalance,
|
BotTradingBalance = entity.BotTradingBalance,
|
||||||
|
BacktestId = entity.BacktestId,
|
||||||
MasterBotUserId = entity.MasterBotUserId,
|
MasterBotUserId = entity.MasterBotUserId,
|
||||||
MasterBotUser = entity.MasterBotUser != null ? Map(entity.MasterBotUser) : null
|
MasterBotUser = entity.MasterBotUser != null ? Map(entity.MasterBotUser) : null
|
||||||
};
|
};
|
||||||
@@ -830,6 +831,7 @@ public static class PostgreSqlMappers
|
|||||||
LongPositionCount = bot.LongPositionCount,
|
LongPositionCount = bot.LongPositionCount,
|
||||||
ShortPositionCount = bot.ShortPositionCount,
|
ShortPositionCount = bot.ShortPositionCount,
|
||||||
BotTradingBalance = bot.BotTradingBalance,
|
BotTradingBalance = bot.BotTradingBalance,
|
||||||
|
BacktestId = bot.BacktestId,
|
||||||
MasterBotUserId = bot.MasterBotUserId,
|
MasterBotUserId = bot.MasterBotUserId,
|
||||||
UpdatedAt = DateTime.UtcNow
|
UpdatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user