6e12dd73c3
New /anomalies/compare page runs every saved strategy preset over the same window and ranks them by ROI — bets, W–L, hit-rate, net, and max drawdown side by side, with the best ROI flagged. Auto-runs on load with an optional date-range refine. - CompareStrategiesUseCase fans RunBacktestUseCase over saved presets (re-loads the anomaly set per preset — fine for the handful a user keeps; stays bug-for-bug identical to a single backtest run). - StrategyComparisonService.BuildVm (pure) computes per-row hit-rate + a single best-by-ROI flag; nav entry + en/ru resx. - 6 tests: use-case fan-out + BuildVm best/tie/no-bets/hit-rate.
49 lines
1.9 KiB
C#
49 lines
1.9 KiB
C#
using FluentAssertions;
|
|
using Marathon.Application.Abstractions;
|
|
using Marathon.Application.UseCases;
|
|
using Marathon.Domain.Backtesting;
|
|
using Marathon.Domain.Entities;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NSubstitute;
|
|
|
|
namespace Marathon.Application.Tests.UseCases;
|
|
|
|
public sealed class CompareStrategiesUseCaseTests
|
|
{
|
|
private readonly ISavedStrategyRepository _strategies = Substitute.For<ISavedStrategyRepository>();
|
|
private readonly IAnomalyRepository _anomalies = Substitute.For<IAnomalyRepository>();
|
|
private readonly IEventRepository _events = Substitute.For<IEventRepository>();
|
|
private readonly IResultRepository _results = Substitute.For<IResultRepository>();
|
|
|
|
private CompareStrategiesUseCase CreateSut()
|
|
{
|
|
var backtest = new RunBacktestUseCase(_anomalies, _events, _results, NullLogger<RunBacktestUseCase>.Instance);
|
|
return new CompareStrategiesUseCase(_strategies, backtest, NullLogger<CompareStrategiesUseCase>.Instance);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Empty_When_NoPresets()
|
|
{
|
|
_strategies.ListAsync(Arg.Any<CancellationToken>()).Returns(Array.Empty<SavedStrategy>());
|
|
|
|
(await CreateSut().ExecuteAsync(null)).Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RunsEachPreset_PreservingNameAndOrder()
|
|
{
|
|
_strategies.ListAsync(Arg.Any<CancellationToken>()).Returns(new[]
|
|
{
|
|
SavedStrategy.Create("Alpha", BacktestStrategy.Default),
|
|
SavedStrategy.Create("Beta", BacktestStrategy.Default),
|
|
});
|
|
// No anomalies → each backtest returns a 0-bet result; events/results are never queried.
|
|
_anomalies.ListAsync(Arg.Any<CancellationToken>()).Returns(Array.Empty<Anomaly>());
|
|
|
|
var rows = await CreateSut().ExecuteAsync(null);
|
|
|
|
rows.Select(r => r.Name).Should().ContainInOrder("Alpha", "Beta");
|
|
rows.Should().OnlyContain(r => r.Result.BetsPlaced == 0);
|
|
}
|
|
}
|