Files
maraphon-app/src/Marathon.UI/Services/UiServicesExtensions.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

76 lines
3.7 KiB
C#

using Marathon.Application.Storage;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MudBlazor.Services;
namespace Marathon.UI.Services;
/// <summary>
/// DI registration helpers for the Marathon.UI Razor Class Library.
/// Hosts call <see cref="AddMarathonUi(IServiceCollection, IConfiguration, string)"/>
/// during startup.
/// </summary>
public static class UiServicesExtensions
{
/// <summary>
/// Registers MudBlazor services, localization, the theme/locale observable
/// state objects, the file-backed settings writer, and binds all
/// configuration sections that the Settings page surfaces.
/// </summary>
/// <param name="services">DI container.</param>
/// <param name="configuration">Host configuration root.</param>
/// <param name="settingsLocalPath">
/// Absolute path to <c>appsettings.Local.json</c>, used by the writer.
/// </param>
public static IServiceCollection AddMarathonUi(
this IServiceCollection services,
IConfiguration configuration,
string settingsLocalPath)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
ArgumentException.ThrowIfNullOrEmpty(settingsLocalPath);
services.AddMudServices();
// No ResourcesPath: the SharedResource type already lives in the
// Marathon.UI.Resources namespace, so its compiled .resources name
// is "Marathon.UI.Resources.SharedResource.{culture}.resources" which
// matches the type's FullName directly. Setting ResourcesPath="Resources"
// here would cause the resolver to look for "...Resources.Resources..."
// and silently fall back to displaying the keys.
services.AddLocalization();
// Strongly typed options bound to appsettings.json sections.
services.Configure<LocalizationOptions>(configuration.GetSection(LocalizationOptions.SectionName));
services.Configure<WorkerOptions>(configuration.GetSection(WorkerOptions.SectionName));
services.Configure<AnomalyOptions>(configuration.GetSection(AnomalyOptions.SectionName));
services.Configure<StorageOptions>(configuration.GetSection(StorageOptions.SectionName));
services.Configure<ScrapingSettingsForm>(configuration.GetSection(ScrapingSettingsForm.SectionName));
services.Configure<PaperTradingSettingsForm>(configuration.GetSection(PaperTradingSettingsForm.SectionName));
// Singletons that drive UI chrome state.
services.AddSingleton<ThemeState>();
services.AddSingleton<LocaleState>();
services.AddSingleton<EventBrowsingState>();
services.AddSingleton<AnomalyBrowsingState>();
// Browsing facades — Scoped so they capture the per-circuit repository scope.
services.AddScoped<IEventBrowsingService, EventBrowsingService>();
services.AddScoped<IAnomalyBrowsingService, AnomalyBrowsingService>();
services.AddScoped<IAnomalyInsightsService, AnomalyInsightsService>();
services.AddScoped<IResultsBrowsingService, ResultsBrowsingService>();
services.AddScoped<IBetJournalService, BetJournalService>();
services.AddScoped<IBacktestService, BacktestService>();
services.AddScoped<IStrategyComparisonService, StrategyComparisonService>();
services.AddScoped<IDashboardSummaryService, DashboardSummaryService>();
services.AddScoped<IPipelineHealthService, PipelineHealthService>();
services.AddScoped<IPaperTradingService, PaperTradingService>();
// Settings writer — file path is host-resolved.
services.AddSingleton<ISettingsWriter>(_ => new JsonSettingsWriter(settingsLocalPath));
return services;
}
}