Files
maraphon-app/tests/Marathon.Application.Tests/UseCases/CompareStrategiesUseCaseTests.cs
T
alexei.dolgolyov 6e12dd73c3 feat(backtest): strategy comparison (head-to-head)
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.
2026-05-29 11:32:01 +03:00

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);
}
}