diff --git a/src/Managing.Api/Controllers/BacktestController.cs b/src/Managing.Api/Controllers/BacktestController.cs
index 83a32364..e11d45d1 100644
--- a/src/Managing.Api/Controllers/BacktestController.cs
+++ b/src/Managing.Api/Controllers/BacktestController.cs
@@ -538,7 +538,7 @@ public class BacktestController : BaseController
if (request.Config.Scenario != null)
{
// Convert ScenarioRequest to Scenario domain object
- scenario = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LoopbackPeriod)
+ scenario = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LookbackPeriod)
{
User = user
};
diff --git a/src/Managing.Api/Controllers/BotController.cs b/src/Managing.Api/Controllers/BotController.cs
index 607ea2c9..444bcee3 100644
--- a/src/Managing.Api/Controllers/BotController.cs
+++ b/src/Managing.Api/Controllers/BotController.cs
@@ -1,4 +1,4 @@
-using Managing.Api.Models.Requests;
+using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
@@ -430,7 +430,7 @@ public class BotController : BaseController
/// Filter by ticker (partial match, case-insensitive). If null, no ticker filtering is applied.
/// Filter by agent name (partial match, case-insensitive). If null, no agent name filtering is applied.
/// Sort field. Valid values: "Name", "Ticker", "Status", "CreateDate", "StartupTime", "Pnl", "WinRate", "AgentName". Default is "CreateDate".
- /// Sort direction. Default is "Desc".
+ /// Sort direction. Default is Desc.
/// A paginated response containing trading bots
[HttpGet]
[Route("Paginated")]
@@ -442,7 +442,7 @@ public class BotController : BaseController
string? ticker = null,
string? agentName = null,
BotSortableColumn sortBy = BotSortableColumn.CreateDate,
- string sortDirection = "Desc")
+ SortDirection sortDirection = SortDirection.Desc)
{
try
{
@@ -770,7 +770,7 @@ public class BotController : BaseController
if (request.Config.Scenario != null)
{
// Convert ScenarioRequest to Scenario domain object
- scenarioForUpdate = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LoopbackPeriod)
+ scenarioForUpdate = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LookbackPeriod)
{
User = user
};
@@ -931,7 +931,7 @@ public class BotController : BaseController
if (request.Config.Scenario != null)
{
// Convert ScenarioRequest to Scenario domain object
- scenario = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LoopbackPeriod)
+ scenario = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LookbackPeriod)
{
User = user
};
diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs
index 4da9429f..8b59bfe7 100644
--- a/src/Managing.Api/Controllers/DataController.cs
+++ b/src/Managing.Api/Controllers/DataController.cs
@@ -1,4 +1,4 @@
-using Managing.Api.Models.Requests;
+using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
@@ -561,7 +561,7 @@ public class DataController : ControllerBase
// Use caching for position data in UI context (not critical trading operations)
var cacheKey = $"positions_{strategy.Identifier}";
var cachedPositions = _cacheService.GetValue>(cacheKey);
-
+
List positions;
if (cachedPositions != null)
{
@@ -571,7 +571,7 @@ public class DataController : ControllerBase
{
// Fetch positions associated with this bot using the provided trading service
positions = (await tradingService.GetPositionsByInitiatorIdentifierAsync(strategy.Identifier)).ToList();
-
+
// Cache positions for 2 minutes for UI display purposes
_cacheService.SaveValue(cacheKey, positions, TimeSpan.FromMinutes(2));
}
@@ -619,9 +619,9 @@ public class DataController : ControllerBase
NetPnL = strategy.NetPnL,
ROIPercentage = strategy.Roi,
Runtime = strategy.Status == BotStatus.Running ? strategy.LastStartTime : null,
- LastStartTime = strategy.LastStartTime,
- LastStopTime = strategy.LastStopTime,
- AccumulatedRunTimeSeconds = strategy.AccumulatedRunTimeSeconds,
+ LastStartTime = strategy.LastStartTime,
+ LastStopTime = strategy.LastStopTime,
+ AccumulatedRunTimeSeconds = strategy.AccumulatedRunTimeSeconds,
TotalRuntimeSeconds = strategy.GetTotalRuntimeSeconds(),
WinRate = winRate,
TotalVolumeTraded = totalVolume,
@@ -715,7 +715,9 @@ public class DataController : ControllerBase
var showOnlyProfitable = _configuration.GetValue("showOnlyProfitable", false);
// Get paginated results from database
- var command = new GetPaginatedAgentSummariesCommand(page, pageSize, sortBy, sortOrder, agentNamesList, showOnlyProfitable);
+ var command =
+ new GetPaginatedAgentSummariesCommand(page, pageSize, sortBy, sortOrder, agentNamesList,
+ showOnlyProfitable);
var result = await _mediator.Send(command);
var agentSummaries = result.Results;
var totalCount = result.TotalCount;
@@ -819,7 +821,7 @@ public class DataController : ControllerBase
/// A domain Scenario object.
private Scenario MapScenarioRequestToScenario(ScenarioRequest scenarioRequest)
{
- var scenario = new Scenario(scenarioRequest.Name, scenarioRequest.LoopbackPeriod);
+ var scenario = new Scenario(scenarioRequest.Name, scenarioRequest.LookbackPeriod);
foreach (var indicatorRequest in scenarioRequest.Indicators)
{
@@ -907,7 +909,7 @@ public class DataController : ControllerBase
/// Filter by ticker (partial match, case-insensitive)
/// Filter by agent name (partial match, case-insensitive)
/// Sort field (defaults to CreateDate)
- /// Sort direction - "Asc" or "Desc" (defaults to "Desc")
+ /// Sort direction - Asc or Desc (defaults to Desc)
/// A paginated list of strategies excluding Saved status bots
[HttpGet("GetStrategiesPaginated")]
public async Task>> GetStrategiesPaginated(
@@ -917,7 +919,7 @@ public class DataController : ControllerBase
string? ticker = null,
string? agentName = null,
BotSortableColumn sortBy = BotSortableColumn.CreateDate,
- string sortDirection = "Desc")
+ SortDirection sortDirection = SortDirection.Desc)
{
// Validate pagination parameters
if (pageNumber < 1)
@@ -930,11 +932,6 @@ public class DataController : ControllerBase
return BadRequest("Page size must be between 1 and 100");
}
- // Validate sort direction
- if (sortDirection != "Asc" && sortDirection != "Desc")
- {
- return BadRequest("Sort direction must be 'Asc' or 'Desc'");
- }
try
{
diff --git a/src/Managing.Api/Controllers/ScenarioController.cs b/src/Managing.Api/Controllers/ScenarioController.cs
index 7d26529c..b0a968b1 100644
--- a/src/Managing.Api/Controllers/ScenarioController.cs
+++ b/src/Managing.Api/Controllers/ScenarioController.cs
@@ -191,7 +191,7 @@ public class ScenarioController : BaseController
return new ScenarioViewModel
{
Name = scenario.Name,
- LoopbackPeriod = scenario.LoopbackPeriod,
+ LoopbackPeriod = scenario.LookbackPeriod,
UserName = scenario.User?.Name,
Indicators = scenario.Indicators?.Select(MapToIndicatorViewModel).ToList() ?? new List()
};
diff --git a/src/Managing.Api/Models/Requests/GetBotsPaginatedRequest.cs b/src/Managing.Api/Models/Requests/GetBotsPaginatedRequest.cs
index 05c83de3..5ecebbe6 100644
--- a/src/Managing.Api/Models/Requests/GetBotsPaginatedRequest.cs
+++ b/src/Managing.Api/Models/Requests/GetBotsPaginatedRequest.cs
@@ -49,7 +49,7 @@ public class GetBotsPaginatedRequest
public BotSortableColumn SortBy { get; set; } = BotSortableColumn.CreateDate;
///
- /// Sort direction. Default is "Desc" (descending).
+ /// Sort direction. Default is Desc (descending).
///
- public string SortDirection { get; set; } = "Desc";
+ public SortDirection SortDirection { get; set; } = SortDirection.Desc;
}
\ No newline at end of file
diff --git a/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs b/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs
index 3a1ca986..37a7c669 100644
--- a/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs
+++ b/src/Managing.Api/Models/Responses/UserStrategyDetailsViewModel.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel.DataAnnotations;
using Managing.Common;
namespace Managing.Api.Models.Responses
@@ -10,41 +11,49 @@ namespace Managing.Api.Models.Responses
///
/// Name of the deployed strategy
///
+ [Required]
public string Name { get; set; }
///
/// Current state of the strategy (RUNNING, STOPPED, UNUSED)
///
+ [Required]
public Enums.BotStatus State { get; set; }
///
/// Total profit or loss generated by the strategy in USD (gross, before fees)
///
+ [Required]
public decimal PnL { get; set; }
///
/// Net profit or loss generated by the strategy in USD (after fees)
///
+ [Required]
public decimal NetPnL { get; set; }
///
/// Return on investment percentage
///
+ [Required]
public decimal ROIPercentage { get; set; }
///
/// Date and time when the strategy was started (only present when running, for live ticker)
///
+ [Required]
public DateTime? Runtime { get; set; }
///
/// Total accumulated runtime in seconds (including current session if running)
///
+ [Required]
public long TotalRuntimeSeconds { get; set; }
///
/// Time when the current or last session started
///
+ [Required]
public DateTime? LastStartTime { get; set; }
///
@@ -55,42 +64,49 @@ namespace Managing.Api.Models.Responses
///
/// Total accumulated runtime across all past sessions (seconds)
///
+ [Required]
public long AccumulatedRunTimeSeconds { get; set; }
///
/// Average percentage of successful trades
///
+ [Required]
public int WinRate { get; set; }
///
/// Total trading volume for all trades
///
+ [Required]
public decimal TotalVolumeTraded { get; set; }
///
/// Trading volume in the last 24 hours
///
+ [Required]
public decimal VolumeLast24H { get; set; }
///
/// Number of winning trades
///
+ [Required]
public int Wins { get; set; }
///
/// Number of losing trades
///
+ [Required]
public int Losses { get; set; }
///
/// Dictionary of all positions executed by this strategy, keyed by position identifier
///
+ [Required]
public List Positions { get; set; } = new List();
- public Guid Identifier { get; set; }
+ [Required] public Guid Identifier { get; set; }
public Dictionary WalletBalances { get; set; } = new Dictionary();
- public Enums.Ticker Ticker { get; set; }
+ [Required] public Enums.Ticker Ticker { get; set; }
///
/// The agent name of the master bot's owner (for copy trading bots)
diff --git a/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs b/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs
index 54c31134..100c7ab6 100644
--- a/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs
+++ b/src/Managing.Application.Abstractions/Repositories/IBotRepository.cs
@@ -37,7 +37,7 @@ public interface IBotRepository
string? ticker = null,
string? agentName = null,
BotSortableColumn sortBy = BotSortableColumn.CreateDate,
- string sortDirection = "Desc",
+ SortDirection sortDirection = SortDirection.Desc,
bool showOnlyProfitable = false);
///
diff --git a/src/Managing.Application.Tests/BacktestTests.cs b/src/Managing.Application.Tests/BacktestTests.cs
index 49806621..4055e1cd 100644
--- a/src/Managing.Application.Tests/BacktestTests.cs
+++ b/src/Managing.Application.Tests/BacktestTests.cs
@@ -158,7 +158,7 @@ public class BacktestTests : BaseTests
var scenario = new Scenario("ETH_BacktestScenario");
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
scenario.Indicators = new List { (IndicatorBase)rsiDivIndicator };
- scenario.LoopbackPeriod = 15;
+ scenario.LookbackPeriod = 15;
var config = new TradingBotConfig
{
diff --git a/src/Managing.Application/Abstractions/IBotService.cs b/src/Managing.Application/Abstractions/IBotService.cs
index 92999b2f..f0d211a2 100644
--- a/src/Managing.Application/Abstractions/IBotService.cs
+++ b/src/Managing.Application/Abstractions/IBotService.cs
@@ -47,7 +47,7 @@ public interface IBotService
/// Filter by ticker (partial match, case-insensitive)
/// Filter by agent name (partial match, case-insensitive)
/// Sort field
- /// Sort direction ("Asc" or "Desc")
+ /// Sort direction
/// Whether to show only profitable bots (ROI > 0)
/// Tuple containing the bots for the current page and total count
Task<(IEnumerable Bots, int TotalCount)> GetBotsPaginatedAsync(
@@ -58,7 +58,7 @@ public interface IBotService
string? ticker = null,
string? agentName = null,
BotSortableColumn sortBy = BotSortableColumn.CreateDate,
- string sortDirection = "Desc",
+ SortDirection sortDirection = SortDirection.Desc,
bool showOnlyProfitable = false);
///
diff --git a/src/Managing.Application/Abstractions/IScenarioService.cs b/src/Managing.Application/Abstractions/IScenarioService.cs
index f4a78792..e4027162 100644
--- a/src/Managing.Application/Abstractions/IScenarioService.cs
+++ b/src/Managing.Application/Abstractions/IScenarioService.cs
@@ -7,7 +7,7 @@ namespace Managing.Application.Abstractions
{
public interface IScenarioService
{
- Task CreateScenario(string name, List strategies, int? loopbackPeriod = 1);
+ Task CreateScenario(string name, List strategies, int loopbackPeriod = 1);
Task> GetIndicatorsAsync();
Task UpdateScenario(string name, List strategies, int? loopbackPeriod);
diff --git a/src/Managing.Application/Backtests/BacktestJobService.cs b/src/Managing.Application/Backtests/BacktestJobService.cs
index bf40f15e..40e33070 100644
--- a/src/Managing.Application/Backtests/BacktestJobService.cs
+++ b/src/Managing.Application/Backtests/BacktestJobService.cs
@@ -151,7 +151,7 @@ public class JobService
if (backtestRequest.Config.Scenario != null)
{
var sReq = backtestRequest.Config.Scenario;
- scenario = new LightScenario(sReq.Name, sReq.LoopbackPeriod)
+ scenario = new LightScenario(sReq.Name, sReq.LookbackPeriod)
{
Indicators = sReq.Indicators?.Select(ind => new LightIndicator(ind.Name, ind.Type)
{
diff --git a/src/Managing.Application/Bots/BacktestFuturesBot.cs b/src/Managing.Application/Bots/BacktestFuturesBot.cs
index f6a6d5cb..c8ffc767 100644
--- a/src/Managing.Application/Bots/BacktestFuturesBot.cs
+++ b/src/Managing.Application/Bots/BacktestFuturesBot.cs
@@ -176,7 +176,7 @@ public class BacktestFuturesBot : TradingBotBase, ITradingBot
throw new ArgumentNullException(nameof(Config.Scenario), "Config.Scenario cannot be null");
// Use TradingBox.GetSignal for backtest with pre-calculated indicators
- var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LoopbackPeriod,
+ var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LookbackPeriod,
preCalculatedIndicatorValues);
if (backtestSignal == null) return;
diff --git a/src/Managing.Application/Bots/BacktestSpotBot.cs b/src/Managing.Application/Bots/BacktestSpotBot.cs
index b08163f9..b8263f83 100644
--- a/src/Managing.Application/Bots/BacktestSpotBot.cs
+++ b/src/Managing.Application/Bots/BacktestSpotBot.cs
@@ -180,7 +180,7 @@ public class BacktestSpotBot : TradingBotBase, ITradingBot
throw new ArgumentNullException(nameof(Config.Scenario), "Config.Scenario cannot be null");
// Use TradingBox.GetSignal for backtest with pre-calculated indicators
- var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LoopbackPeriod,
+ var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LookbackPeriod,
preCalculatedIndicatorValues);
if (backtestSignal == null) return;
diff --git a/src/Managing.Application/ManageBot/BotService.cs b/src/Managing.Application/ManageBot/BotService.cs
index 4f1ddab8..82fa965f 100644
--- a/src/Managing.Application/ManageBot/BotService.cs
+++ b/src/Managing.Application/ManageBot/BotService.cs
@@ -487,7 +487,7 @@ namespace Managing.Application.ManageBot
string? ticker = null,
string? agentName = null,
BotSortableColumn sortBy = BotSortableColumn.CreateDate,
- string sortDirection = "Desc",
+ SortDirection sortDirection = SortDirection.Desc,
bool showOnlyProfitable = false)
{
return await ServiceScopeHelpers.WithScopedService Bots, int TotalCount)>(
diff --git a/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs b/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs
index b872b5ca..b92723a9 100644
--- a/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs
+++ b/src/Managing.Application/Scenarios/ScenarioRunnerGrain.cs
@@ -68,7 +68,7 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain
// Get candles as ordered List (already ordered by date from CandleStoreGrain)
var candlesList = await GetCandlesAsync(tradingExchanges, config);
-
+
if (candlesList.Count == 0)
{
_logger.LogWarning($"No candles available for {config.Ticker} for {config.Name}");
@@ -88,7 +88,7 @@ public class ScenarioRunnerGrain : Grain, IScenarioRunnerGrain
candlesList,
config.Scenario,
previousSignals,
- config.Scenario?.LoopbackPeriod ?? 1);
+ config.Scenario?.LookbackPeriod ?? 1);
if (signal != null && signal.Date > candle.Date)
{
diff --git a/src/Managing.Application/Scenarios/ScenarioService.cs b/src/Managing.Application/Scenarios/ScenarioService.cs
index d9582957..9cf469cb 100644
--- a/src/Managing.Application/Scenarios/ScenarioService.cs
+++ b/src/Managing.Application/Scenarios/ScenarioService.cs
@@ -20,7 +20,7 @@ namespace Managing.Application.Scenarios
_tradingService = tradingService;
}
- public async Task CreateScenario(string name, List strategies, int? loopbackPeriod = 1)
+ public async Task CreateScenario(string name, List strategies, int loopbackPeriod = 1)
{
var scenario = new Scenario(name, loopbackPeriod);
@@ -77,7 +77,7 @@ namespace Managing.Application.Scenarios
scenario.AddIndicator(await _tradingService.GetIndicatorByNameAsync(strategy));
}
- scenario.LoopbackPeriod = loopbackPeriod ?? 1;
+ scenario.LookbackPeriod = loopbackPeriod ?? 1;
await _tradingService.UpdateScenarioAsync(scenario);
return true;
}
@@ -262,7 +262,7 @@ namespace Managing.Application.Scenarios
}
scenario.Indicators.Clear();
- scenario.LoopbackPeriod = loopbackPeriod ?? 1;
+ scenario.LookbackPeriod = loopbackPeriod ?? 1;
foreach (var strategyName in strategies)
{
diff --git a/src/Managing.Application/Workers/BundleBacktestWorker.cs b/src/Managing.Application/Workers/BundleBacktestWorker.cs
index d469afbc..63e50413 100644
--- a/src/Managing.Application/Workers/BundleBacktestWorker.cs
+++ b/src/Managing.Application/Workers/BundleBacktestWorker.cs
@@ -225,7 +225,7 @@ public class BundleBacktestWorker : BaseWorker
if (runBacktestRequest.Config.Scenario != null)
{
var sReq = runBacktestRequest.Config.Scenario;
- scenario = new LightScenario(sReq.Name, sReq.LoopbackPeriod)
+ scenario = new LightScenario(sReq.Name, sReq.LookbackPeriod)
{
Indicators = sReq.Indicators?.Select(i => new LightIndicator(i.Name, i.Type)
{
diff --git a/src/Managing.Common/Enums.cs b/src/Managing.Common/Enums.cs
index e5a0fb76..a8d0c4ef 100644
--- a/src/Managing.Common/Enums.cs
+++ b/src/Managing.Common/Enums.cs
@@ -1,4 +1,4 @@
-namespace Managing.Common;
+namespace Managing.Common;
public static class Enums
{
@@ -101,6 +101,15 @@ public static class Enums
AgentName
}
+ ///
+ /// Sort direction for pagination endpoints
+ ///
+ public enum SortDirection
+ {
+ Asc,
+ Desc
+ }
+
public enum SignalStatus
{
WaitingForPosition,
diff --git a/src/Managing.Domain/Backtests/ScenarioRequest.cs b/src/Managing.Domain/Backtests/ScenarioRequest.cs
index 547d8fe0..57e54878 100644
--- a/src/Managing.Domain/Backtests/ScenarioRequest.cs
+++ b/src/Managing.Domain/Backtests/ScenarioRequest.cs
@@ -22,5 +22,5 @@ public class ScenarioRequest
///
/// The loopback period for the scenario
///
- public int? LoopbackPeriod { get; set; }
-}
\ No newline at end of file
+ public int LookbackPeriod { get; set; }
+}
\ No newline at end of file
diff --git a/src/Managing.Domain/Bots/TradingBotConfig.cs b/src/Managing.Domain/Bots/TradingBotConfig.cs
index c5cc0c3f..68e3c62d 100644
--- a/src/Managing.Domain/Bots/TradingBotConfig.cs
+++ b/src/Managing.Domain/Bots/TradingBotConfig.cs
@@ -45,6 +45,7 @@ public class TradingBotConfig
/// Orleans-friendly version without FixedSizeQueue and User properties.
///
[Id(12)]
+ [Required]
public LightScenario Scenario { get; set; }
///
@@ -104,6 +105,7 @@ public class TradingBotConfig
///
[Id(20)]
public bool UseForDynamicStopLoss { get; set; } = true;
+
///
/// Parameter to indicate if the bot is for copy trading
///
diff --git a/src/Managing.Domain/Indicators/LightIndicator.cs b/src/Managing.Domain/Indicators/LightIndicator.cs
index 1e8a35a9..1f75d210 100644
--- a/src/Managing.Domain/Indicators/LightIndicator.cs
+++ b/src/Managing.Domain/Indicators/LightIndicator.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel.DataAnnotations;
using Managing.Domain.Scenarios;
using Orleans;
using static Managing.Common.Enums;
@@ -18,11 +19,11 @@ public class LightIndicator
SignalType = ScenarioHelpers.GetSignalType(type);
}
- [Id(0)] public string Name { get; set; }
+ [Id(0)] [Required] public string Name { get; set; }
- [Id(1)] public IndicatorType Type { get; set; }
+ [Id(1)] [Required] public IndicatorType Type { get; set; }
- [Id(2)] public SignalType SignalType { get; set; }
+ [Id(2)] [Required] public SignalType SignalType { get; set; }
[Id(3)] public int MinimumHistory { get; set; }
diff --git a/src/Managing.Domain/Scenarios/LightScenario.cs b/src/Managing.Domain/Scenarios/LightScenario.cs
index 692526cc..31625a60 100644
--- a/src/Managing.Domain/Scenarios/LightScenario.cs
+++ b/src/Managing.Domain/Scenarios/LightScenario.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel.DataAnnotations;
using Managing.Domain.Strategies;
using Orleans;
@@ -10,25 +11,25 @@ namespace Managing.Domain.Scenarios;
[GenerateSerializer]
public class LightScenario
{
- public LightScenario(string name, int? loopbackPeriod = 1)
+ public LightScenario(string name, int lookbackPeriod = 1)
{
Name = name;
Indicators = new List();
- LoopbackPeriod = loopbackPeriod;
+ LookbackPeriod = lookbackPeriod;
}
[Id(0)] public string Name { get; set; }
[Id(1)] public List Indicators { get; set; }
- [Id(2)] public int? LoopbackPeriod { get; set; }
+ [Id(2)] [Required] public int LookbackPeriod { get; set; }
///
/// Converts a full Scenario to a LightScenario
///
public static LightScenario FromScenario(Scenario scenario)
{
- var lightScenario = new LightScenario(scenario.Name, scenario.LoopbackPeriod)
+ var lightScenario = new LightScenario(scenario.Name, scenario.LookbackPeriod)
{
Indicators = scenario.Indicators?.Select(ScenarioHelpers.BaseToLight).ToList() ??
new List()
@@ -41,7 +42,7 @@ public class LightScenario
///
public Scenario ToScenario()
{
- var scenario = new Scenario(Name, LoopbackPeriod)
+ var scenario = new Scenario(Name, LookbackPeriod)
{
Indicators = Indicators?.Select(li => li.LightToBase()).ToList()
};
diff --git a/src/Managing.Domain/Scenarios/Scenario.cs b/src/Managing.Domain/Scenarios/Scenario.cs
index 58571371..88558b11 100644
--- a/src/Managing.Domain/Scenarios/Scenario.cs
+++ b/src/Managing.Domain/Scenarios/Scenario.cs
@@ -7,18 +7,18 @@ namespace Managing.Domain.Scenarios
[GenerateSerializer]
public class Scenario
{
- public Scenario(string name, int? loopbackPeriod = 1)
+ public Scenario(string name, int lookbackPeriod = 1)
{
Name = name;
Indicators = new List();
- LoopbackPeriod = loopbackPeriod;
+ LookbackPeriod = lookbackPeriod;
}
[Id(0)] public string Name { get; set; }
[Id(1)] public List Indicators { get; set; }
- [Id(2)] public int? LoopbackPeriod { get; set; }
+ [Id(2)] public int LookbackPeriod { get; set; }
[Id(3)] public User User { get; set; }
diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs
index 627923ae..5e33894a 100644
--- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs
+++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlBotRepository.cs
@@ -206,7 +206,7 @@ public class PostgreSqlBotRepository : IBotRepository
string? ticker = null,
string? agentName = null,
BotSortableColumn sortBy = BotSortableColumn.CreateDate,
- string sortDirection = "Desc",
+ SortDirection sortDirection = SortDirection.Desc,
bool showOnlyProfitable = false)
{
// Build the query with filters
@@ -249,33 +249,33 @@ public class PostgreSqlBotRepository : IBotRepository
// Apply sorting
query = sortBy switch
{
- BotSortableColumn.Name => sortDirection.ToLower() == "asc"
+ BotSortableColumn.Name => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.Name)
: query.OrderByDescending(b => b.Name),
- BotSortableColumn.Ticker => sortDirection.ToLower() == "asc"
+ BotSortableColumn.Ticker => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.Ticker)
: query.OrderByDescending(b => b.Ticker),
- BotSortableColumn.Status => sortDirection.ToLower() == "asc"
+ BotSortableColumn.Status => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.Status)
: query.OrderByDescending(b => b.Status),
- BotSortableColumn.StartupTime => sortDirection.ToLower() == "asc"
+ BotSortableColumn.StartupTime => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.StartupTime)
: query.OrderByDescending(b => b.StartupTime),
- BotSortableColumn.Roi => sortDirection.ToLower() == "asc"
+ BotSortableColumn.Roi => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.Roi)
: query.OrderByDescending(b => b.Roi),
- BotSortableColumn.Pnl => sortDirection.ToLower() == "asc"
+ BotSortableColumn.Pnl => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.Pnl)
: query.OrderByDescending(b => b.Pnl),
- BotSortableColumn.WinRate => sortDirection.ToLower() == "asc"
+ BotSortableColumn.WinRate => sortDirection == SortDirection.Asc
? query.OrderBy(b =>
(b.TradeWins + b.TradeLosses) > 0 ? (double)b.TradeWins / (b.TradeWins + b.TradeLosses) : 0)
: query.OrderByDescending(b =>
(b.TradeWins + b.TradeLosses) > 0 ? (double)b.TradeWins / (b.TradeWins + b.TradeLosses) : 0),
- BotSortableColumn.AgentName => sortDirection.ToLower() == "asc"
+ BotSortableColumn.AgentName => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.User.AgentName)
: query.OrderByDescending(b => b.User.AgentName),
- _ => sortDirection.ToLower() == "asc"
+ _ => sortDirection == SortDirection.Asc
? query.OrderBy(b => b.CreateDate)
: query.OrderByDescending(b => b.CreateDate)
};
diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs
index 019f5ad8..1cf2fe12 100644
--- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs
+++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlMappers.cs
@@ -360,7 +360,9 @@ public static class PostgreSqlMappers
Duration = backtest.EndDate - backtest.StartDate,
MoneyManagementJson = JsonConvert.SerializeObject(backtest.Config?.MoneyManagement, jsonSettings),
UserId = backtest.User?.Id ?? 0,
- StatisticsJson = backtest.Statistics != null ? JsonConvert.SerializeObject(backtest.Statistics, jsonSettings) : null,
+ StatisticsJson = backtest.Statistics != null
+ ? JsonConvert.SerializeObject(backtest.Statistics, jsonSettings)
+ : null,
SharpeRatio = backtest.Statistics?.SharpeRatio ?? 0m,
MaxDrawdown = backtest.Statistics?.MaxDrawdown ?? 0m,
MaxDrawdownRecoveryTime = backtest.Statistics?.MaxDrawdownRecoveryTime ?? TimeSpan.Zero,
@@ -503,7 +505,7 @@ public static class PostgreSqlMappers
return new ScenarioEntity
{
Name = scenario.Name,
- LoopbackPeriod = scenario.LoopbackPeriod ?? 1,
+ LoopbackPeriod = scenario.LookbackPeriod,
UserId = scenario.User?.Id ?? 0
};
}
diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs
index f4d70f8b..dd7340c6 100644
--- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs
+++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs
@@ -13,7 +13,8 @@ namespace Managing.Infrastructure.Databases.PostgreSql;
public class PostgreSqlTradingRepository : BaseRepositoryWithLogging, ITradingRepository
{
- public PostgreSqlTradingRepository(ManagingDbContext context, ILogger logger, SentrySqlMonitoringService sentryMonitoringService)
+ public PostgreSqlTradingRepository(ManagingDbContext context, ILogger logger,
+ SentrySqlMonitoringService sentryMonitoringService)
: base(context, logger, sentryMonitoringService)
{
}
@@ -154,7 +155,7 @@ public class PostgreSqlTradingRepository : BaseRepositoryWithLogging, ITradingRe
if (entity != null)
{
- entity.LoopbackPeriod = scenario.LoopbackPeriod ?? 1;
+ entity.LoopbackPeriod = scenario.LookbackPeriod;
entity.UserId = scenario.User?.Id ?? 0;
entity.UpdatedAt = DateTime.UtcNow;
@@ -408,62 +409,63 @@ public class PostgreSqlTradingRepository : BaseRepositoryWithLogging, ITradingRe
public async Task UpdatePositionAsync(Position position)
{
await ExecuteWithLoggingAsync(async () =>
- {
- try
{
- await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
-
- var entity = _context.Positions
- .AsTracking()
- .Include(p => p.OpenTrade)
- .Include(p => p.StopLossTrade)
- .Include(p => p.TakeProfit1Trade)
- .Include(p => p.TakeProfit2Trade)
- .FirstOrDefault(p => p.Identifier == position.Identifier);
-
- if (entity != null)
+ try
{
- entity.ProfitAndLoss = position.ProfitAndLoss?.Realized ?? 0;
- entity.NetPnL = position.ProfitAndLoss?.Net ?? 0;
- entity.UiFees = position.UiFees;
- // entity.OriginDirection = position.OriginDirection;
- entity.GasFees = position.GasFees;
- entity.Status = position.Status;
- entity.MoneyManagementJson = position.MoneyManagement != null
- ? JsonConvert.SerializeObject(position.MoneyManagement)
- : null;
- entity.UpdatedAt = DateTime.UtcNow;
+ await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
- // Update related trades directly through the position's trade references
- // This ensures we're updating the correct trade records for this specific position
- if (position.Open != null && entity.OpenTrade != null)
+ var entity = _context.Positions
+ .AsTracking()
+ .Include(p => p.OpenTrade)
+ .Include(p => p.StopLossTrade)
+ .Include(p => p.TakeProfit1Trade)
+ .Include(p => p.TakeProfit2Trade)
+ .FirstOrDefault(p => p.Identifier == position.Identifier);
+
+ if (entity != null)
{
- UpdateTradeEntity(entity.OpenTrade, position.Open);
- }
+ entity.ProfitAndLoss = position.ProfitAndLoss?.Realized ?? 0;
+ entity.NetPnL = position.ProfitAndLoss?.Net ?? 0;
+ entity.UiFees = position.UiFees;
+ // entity.OriginDirection = position.OriginDirection;
+ entity.GasFees = position.GasFees;
+ entity.Status = position.Status;
+ entity.MoneyManagementJson = position.MoneyManagement != null
+ ? JsonConvert.SerializeObject(position.MoneyManagement)
+ : null;
+ entity.UpdatedAt = DateTime.UtcNow;
- if (position.StopLoss != null && entity.StopLossTrade != null)
- {
- UpdateTradeEntity(entity.StopLossTrade, position.StopLoss);
- }
+ // Update related trades directly through the position's trade references
+ // This ensures we're updating the correct trade records for this specific position
+ if (position.Open != null && entity.OpenTrade != null)
+ {
+ UpdateTradeEntity(entity.OpenTrade, position.Open);
+ }
- if (position.TakeProfit1 != null && entity.TakeProfit1Trade != null)
- {
- UpdateTradeEntity(entity.TakeProfit1Trade, position.TakeProfit1);
- }
+ if (position.StopLoss != null && entity.StopLossTrade != null)
+ {
+ UpdateTradeEntity(entity.StopLossTrade, position.StopLoss);
+ }
- if (position.TakeProfit2 != null && entity.TakeProfit2Trade != null)
- {
- UpdateTradeEntity(entity.TakeProfit2Trade, position.TakeProfit2);
- }
+ if (position.TakeProfit1 != null && entity.TakeProfit1Trade != null)
+ {
+ UpdateTradeEntity(entity.TakeProfit1Trade, position.TakeProfit1);
+ }
- await _context.SaveChangesAsync();
+ if (position.TakeProfit2 != null && entity.TakeProfit2Trade != null)
+ {
+ UpdateTradeEntity(entity.TakeProfit2Trade, position.TakeProfit2);
+ }
+
+ await _context.SaveChangesAsync();
+ }
}
- }
- finally
- {
- await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
- }
- }, nameof(UpdatePositionAsync), ("positionIdentifier", position.Identifier), ("positionStatus", position.Status));
+ finally
+ {
+ await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
+ }
+ }, nameof(UpdatePositionAsync), ("positionIdentifier", position.Identifier),
+ ("positionStatus", position.Status));
}
///
@@ -474,7 +476,7 @@ public class PostgreSqlTradingRepository : BaseRepositoryWithLogging, ITradingRe
{
// Only update the date if the trade status is changing from Requested to Filled
// This prevents overwriting dates for trades that are already filled
- if (entity.Status != trade.Status)
+ if (entity.Status != trade.Status)
{
entity.Date = trade.Date;
}
diff --git a/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts b/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts
index c6c2deb6..6985348b 100644
--- a/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts
+++ b/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts
@@ -355,7 +355,7 @@ export interface TradingBotConfig {
flipPosition: boolean;
name: string;
riskManagement?: RiskManagement | null;
- scenario?: LightScenario | null;
+ scenario: LightScenario;
scenarioName?: string | null;
maxPositionTimeHours?: number | null;
closeEarlyWhenProfitable?: boolean;
@@ -420,13 +420,13 @@ export enum RiskToleranceLevel {
export interface LightScenario {
name?: string | null;
indicators?: LightIndicator[] | null;
- loopbackPeriod?: number | null;
+ lookbackPeriod: number;
}
export interface LightIndicator {
- name?: string | null;
- type?: IndicatorType;
- signalType?: SignalType;
+ name: string;
+ type: IndicatorType;
+ signalType: SignalType;
minimumHistory?: number;
period?: number | null;
fastPeriods?: number | null;
@@ -728,7 +728,7 @@ export interface TradingBotConfigRequest {
export interface ScenarioRequest {
name: string;
indicators: IndicatorRequest[];
- loopbackPeriod?: number | null;
+ lookbackPeriod?: number;
}
export interface IndicatorRequest {
@@ -1014,6 +1014,11 @@ export enum BotSortableColumn {
AgentName = "AgentName",
}
+export enum SortDirection {
+ Asc = "Asc",
+ Desc = "Desc",
+}
+
export interface CreateManualSignalRequest {
identifier?: string;
direction?: TradeDirection;
@@ -1052,7 +1057,7 @@ export interface Spotlight {
export interface Scenario {
name?: string | null;
indicators?: IndicatorBase[] | null;
- loopbackPeriod?: number | null;
+ lookbackPeriod?: number;
user?: User | null;
}
@@ -1231,25 +1236,25 @@ export interface StrategyRoiPerformance {
}
export interface UserStrategyDetailsViewModel {
- name?: string | null;
- state?: BotStatus;
- pnL?: number;
- netPnL?: number;
- roiPercentage?: number;
- runtime?: Date | null;
- totalRuntimeSeconds?: number;
- lastStartTime?: Date | null;
+ name: string;
+ state: BotStatus;
+ pnL: number;
+ netPnL: number;
+ roiPercentage: number;
+ runtime: Date;
+ totalRuntimeSeconds: number;
+ lastStartTime: Date;
lastStopTime?: Date | null;
- accumulatedRunTimeSeconds?: number;
- winRate?: number;
- totalVolumeTraded?: number;
- volumeLast24H?: number;
- wins?: number;
- losses?: number;
- positions?: PositionViewModel[] | null;
- identifier?: string;
+ accumulatedRunTimeSeconds: number;
+ winRate: number;
+ totalVolumeTraded: number;
+ volumeLast24H: number;
+ wins: number;
+ losses: number;
+ positions: PositionViewModel[];
+ identifier: string;
walletBalances?: { [key: string]: number; } | null;
- ticker?: Ticker;
+ ticker: Ticker;
masterAgentName?: string | null;
}
diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts
index a8a6d37f..0db7d052 100644
--- a/src/Managing.WebApp/src/generated/ManagingApi.ts
+++ b/src/Managing.WebApp/src/generated/ManagingApi.ts
@@ -1917,7 +1917,7 @@ export class BotClient extends AuthorizedApiBase {
return Promise.resolve(null as any);
}
- bot_GetBotsPaginated(pageNumber: number | undefined, pageSize: number | undefined, status: BotStatus | null | undefined, name: string | null | undefined, ticker: string | null | undefined, agentName: string | null | undefined, sortBy: BotSortableColumn | undefined, sortDirection: string | null | undefined): Promise {
+ bot_GetBotsPaginated(pageNumber: number | undefined, pageSize: number | undefined, status: BotStatus | null | undefined, name: string | null | undefined, ticker: string | null | undefined, agentName: string | null | undefined, sortBy: BotSortableColumn | undefined, sortDirection: SortDirection | undefined): Promise {
let url_ = this.baseUrl + "/Bot/Paginated?";
if (pageNumber === null)
throw new Error("The parameter 'pageNumber' cannot be null.");
@@ -1939,7 +1939,9 @@ export class BotClient extends AuthorizedApiBase {
throw new Error("The parameter 'sortBy' cannot be null.");
else if (sortBy !== undefined)
url_ += "sortBy=" + encodeURIComponent("" + sortBy) + "&";
- if (sortDirection !== undefined && sortDirection !== null)
+ if (sortDirection === null)
+ throw new Error("The parameter 'sortDirection' cannot be null.");
+ else if (sortDirection !== undefined)
url_ += "sortDirection=" + encodeURIComponent("" + sortDirection) + "&";
url_ = url_.replace(/[?&]$/, "");
@@ -2599,7 +2601,7 @@ export class DataClient extends AuthorizedApiBase {
return Promise.resolve(null as any);
}
- data_GetStrategiesPaginated(pageNumber: number | undefined, pageSize: number | undefined, name: string | null | undefined, ticker: string | null | undefined, agentName: string | null | undefined, sortBy: BotSortableColumn | undefined, sortDirection: string | null | undefined): Promise {
+ data_GetStrategiesPaginated(pageNumber: number | undefined, pageSize: number | undefined, name: string | null | undefined, ticker: string | null | undefined, agentName: string | null | undefined, sortBy: BotSortableColumn | undefined, sortDirection: SortDirection | undefined): Promise {
let url_ = this.baseUrl + "/Data/GetStrategiesPaginated?";
if (pageNumber === null)
throw new Error("The parameter 'pageNumber' cannot be null.");
@@ -2619,7 +2621,9 @@ export class DataClient extends AuthorizedApiBase {
throw new Error("The parameter 'sortBy' cannot be null.");
else if (sortBy !== undefined)
url_ += "sortBy=" + encodeURIComponent("" + sortBy) + "&";
- if (sortDirection !== undefined && sortDirection !== null)
+ if (sortDirection === null)
+ throw new Error("The parameter 'sortDirection' cannot be null.");
+ else if (sortDirection !== undefined)
url_ += "sortDirection=" + encodeURIComponent("" + sortDirection) + "&";
url_ = url_.replace(/[?&]$/, "");
@@ -4854,7 +4858,7 @@ export interface TradingBotConfig {
flipPosition: boolean;
name: string;
riskManagement?: RiskManagement | null;
- scenario?: LightScenario | null;
+ scenario: LightScenario;
scenarioName?: string | null;
maxPositionTimeHours?: number | null;
closeEarlyWhenProfitable?: boolean;
@@ -4919,13 +4923,13 @@ export enum RiskToleranceLevel {
export interface LightScenario {
name?: string | null;
indicators?: LightIndicator[] | null;
- loopbackPeriod?: number | null;
+ lookbackPeriod: number;
}
export interface LightIndicator {
- name?: string | null;
- type?: IndicatorType;
- signalType?: SignalType;
+ name: string;
+ type: IndicatorType;
+ signalType: SignalType;
minimumHistory?: number;
period?: number | null;
fastPeriods?: number | null;
@@ -5227,7 +5231,7 @@ export interface TradingBotConfigRequest {
export interface ScenarioRequest {
name: string;
indicators: IndicatorRequest[];
- loopbackPeriod?: number | null;
+ lookbackPeriod?: number;
}
export interface IndicatorRequest {
@@ -5513,6 +5517,11 @@ export enum BotSortableColumn {
AgentName = "AgentName",
}
+export enum SortDirection {
+ Asc = "Asc",
+ Desc = "Desc",
+}
+
export interface CreateManualSignalRequest {
identifier?: string;
direction?: TradeDirection;
@@ -5551,7 +5560,7 @@ export interface Spotlight {
export interface Scenario {
name?: string | null;
indicators?: IndicatorBase[] | null;
- loopbackPeriod?: number | null;
+ lookbackPeriod?: number;
user?: User | null;
}
@@ -5730,25 +5739,25 @@ export interface StrategyRoiPerformance {
}
export interface UserStrategyDetailsViewModel {
- name?: string | null;
- state?: BotStatus;
- pnL?: number;
- netPnL?: number;
- roiPercentage?: number;
- runtime?: Date | null;
- totalRuntimeSeconds?: number;
- lastStartTime?: Date | null;
+ name: string;
+ state: BotStatus;
+ pnL: number;
+ netPnL: number;
+ roiPercentage: number;
+ runtime: Date;
+ totalRuntimeSeconds: number;
+ lastStartTime: Date;
lastStopTime?: Date | null;
- accumulatedRunTimeSeconds?: number;
- winRate?: number;
- totalVolumeTraded?: number;
- volumeLast24H?: number;
- wins?: number;
- losses?: number;
- positions?: PositionViewModel[] | null;
- identifier?: string;
+ accumulatedRunTimeSeconds: number;
+ winRate: number;
+ totalVolumeTraded: number;
+ volumeLast24H: number;
+ wins: number;
+ losses: number;
+ positions: PositionViewModel[];
+ identifier: string;
walletBalances?: { [key: string]: number; } | null;
- ticker?: Ticker;
+ ticker: Ticker;
masterAgentName?: string | null;
}
diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts
index c6c2deb6..6985348b 100644
--- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts
+++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts
@@ -355,7 +355,7 @@ export interface TradingBotConfig {
flipPosition: boolean;
name: string;
riskManagement?: RiskManagement | null;
- scenario?: LightScenario | null;
+ scenario: LightScenario;
scenarioName?: string | null;
maxPositionTimeHours?: number | null;
closeEarlyWhenProfitable?: boolean;
@@ -420,13 +420,13 @@ export enum RiskToleranceLevel {
export interface LightScenario {
name?: string | null;
indicators?: LightIndicator[] | null;
- loopbackPeriod?: number | null;
+ lookbackPeriod: number;
}
export interface LightIndicator {
- name?: string | null;
- type?: IndicatorType;
- signalType?: SignalType;
+ name: string;
+ type: IndicatorType;
+ signalType: SignalType;
minimumHistory?: number;
period?: number | null;
fastPeriods?: number | null;
@@ -728,7 +728,7 @@ export interface TradingBotConfigRequest {
export interface ScenarioRequest {
name: string;
indicators: IndicatorRequest[];
- loopbackPeriod?: number | null;
+ lookbackPeriod?: number;
}
export interface IndicatorRequest {
@@ -1014,6 +1014,11 @@ export enum BotSortableColumn {
AgentName = "AgentName",
}
+export enum SortDirection {
+ Asc = "Asc",
+ Desc = "Desc",
+}
+
export interface CreateManualSignalRequest {
identifier?: string;
direction?: TradeDirection;
@@ -1052,7 +1057,7 @@ export interface Spotlight {
export interface Scenario {
name?: string | null;
indicators?: IndicatorBase[] | null;
- loopbackPeriod?: number | null;
+ lookbackPeriod?: number;
user?: User | null;
}
@@ -1231,25 +1236,25 @@ export interface StrategyRoiPerformance {
}
export interface UserStrategyDetailsViewModel {
- name?: string | null;
- state?: BotStatus;
- pnL?: number;
- netPnL?: number;
- roiPercentage?: number;
- runtime?: Date | null;
- totalRuntimeSeconds?: number;
- lastStartTime?: Date | null;
+ name: string;
+ state: BotStatus;
+ pnL: number;
+ netPnL: number;
+ roiPercentage: number;
+ runtime: Date;
+ totalRuntimeSeconds: number;
+ lastStartTime: Date;
lastStopTime?: Date | null;
- accumulatedRunTimeSeconds?: number;
- winRate?: number;
- totalVolumeTraded?: number;
- volumeLast24H?: number;
- wins?: number;
- losses?: number;
- positions?: PositionViewModel[] | null;
- identifier?: string;
+ accumulatedRunTimeSeconds: number;
+ winRate: number;
+ totalVolumeTraded: number;
+ volumeLast24H: number;
+ wins: number;
+ losses: number;
+ positions: PositionViewModel[];
+ identifier: string;
walletBalances?: { [key: string]: number; } | null;
- ticker?: Ticker;
+ ticker: Ticker;
masterAgentName?: string | null;
}
diff --git a/src/Managing.Workers.Tests/BacktestExecutorTests.cs b/src/Managing.Workers.Tests/BacktestExecutorTests.cs
index a1fae7f8..94b9ae9e 100644
--- a/src/Managing.Workers.Tests/BacktestExecutorTests.cs
+++ b/src/Managing.Workers.Tests/BacktestExecutorTests.cs
@@ -112,7 +112,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
var scenario = new Scenario("ETH_BacktestScenario");
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
scenario.Indicators = new List { (IndicatorBase)rsiDivIndicator };
- scenario.LoopbackPeriod = 15;
+ scenario.LookbackPeriod = 15;
var config = new TradingBotConfig
{
@@ -207,7 +207,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
var scenario = new Scenario("ETH_BacktestScenario");
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
scenario.Indicators = new List { (IndicatorBase)rsiDivIndicator };
- scenario.LoopbackPeriod = 15;
+ scenario.LookbackPeriod = 15;
var config = new TradingBotConfig
{
@@ -297,7 +297,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
var scenario = new Scenario("ETH_BacktestScenario");
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
scenario.Indicators = new List { (IndicatorBase)rsiDivIndicator };
- scenario.LoopbackPeriod = 15;
+ scenario.LookbackPeriod = 15;
var config = new TradingBotConfig
{
@@ -387,7 +387,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
var emaCrossIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.EmaCross, "EmaCross", period: 21);
scenario.Indicators = new List
{ (IndicatorBase)rsiDivIndicator, (IndicatorBase)emaCrossIndicator };
- scenario.LoopbackPeriod = 15; // 15 minutes loopback period as requested
+ scenario.LookbackPeriod = 15; // 15 minutes loopback period as requested
var config = new TradingBotConfig
{
@@ -488,7 +488,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
Console.WriteLine($"📈 Win Rate: {result.WinRate}% (Expected: {expectedWinRatePercent}%)");
Console.WriteLine($"📈 Growth: {result.GrowthPercentage:F2}% (Expected: {expectedGrowthPercentage:F2}%)");
Console.WriteLine(
- $"🎭 Scenario: {scenario.Name} ({scenario.Indicators.Count} indicators, LoopbackPeriod: {scenario.LoopbackPeriod})");
+ $"🎭 Scenario: {scenario.Name} ({scenario.Indicators.Count} indicators, LoopbackPeriod: {scenario.LookbackPeriod})");
// Performance assertion - should be reasonably fast even with 2 indicators
Assert.True(candlesPerSecond > 200,
@@ -510,7 +510,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
var scenario = new Scenario("ETH_Spot_BacktestScenario");
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
scenario.Indicators = new List { (IndicatorBase)rsiDivIndicator };
- scenario.LoopbackPeriod = 15;
+ scenario.LookbackPeriod = 15;
var config = new TradingBotConfig
{
@@ -570,10 +570,10 @@ public class BacktestExecutorTests : BaseTests, IDisposable
// Assert - Validate specific backtest results
Assert.NotNull(result);
Assert.IsType(result);
-
+
// Verify TradingType is BacktestSpot
Assert.Equal(TradingType.BacktestSpot, result.Config.TradingType);
-
+
// Validate key metrics - Updated with actual backtest results
Assert.Equal(1000.0m, result.InitialBalance);
Assert.Equal(-71.63m, Math.Round(result.FinalPnl, 2));
@@ -586,7 +586,7 @@ public class BacktestExecutorTests : BaseTests, IDisposable
Assert.Equal(-0.107, Math.Round((double)(result.SharpeRatio ?? 0), 3));
Assert.True(Math.Abs(result.Score - 0.0) < 0.001,
$"Score {result.Score} should be within 0.001 of expected value 0.0");
-
+
// Validate dates
Assert.Equal(new DateTime(2025, 10, 14, 12, 0, 0), result.StartDate);
Assert.Equal(new DateTime(2025, 10, 24, 11, 45, 0), result.EndDate);