using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using Predictalytics.Infrastructure.Data; using Predictalytics.Application.Interfaces; namespace Predictalytics.Worker.Services; public class TraderAnalyticsWorker : BackgroundService { private readonly IServiceProvider _services; private readonly ILogger _logger; public TraderAnalyticsWorker(IServiceProvider services, ILogger _logger) { _services = services; this._logger = _logger; } protected override async Task ExecuteAsync(CancellationToken ct) { _logger.LogInformation("TraderAnalyticsWorker starting..."); while (!ct.IsCancellationRequested) { try { await RunAnalyticsAsync(ct); } catch (Exception ex) { _logger.LogError(ex, "Error in TraderAnalyticsWorker"); } _logger.LogInformation("TraderAnalyticsWorker sleeping for 12 hours..."); await Task.Delay(TimeSpan.FromHours(12), ct); } } private async Task RunAnalyticsAsync(CancellationToken ct) { using var scope = _services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var pnlEngine = scope.ServiceProvider.GetRequiredService(); var cutoff30d = DateTime.UtcNow.AddDays(-30); // Find traders active in the last 30 days var traderIds = await db.Trades .Where(t => t.ExecutedAt >= cutoff30d) .Select(t => t.TraderId) .Distinct() .ToListAsync(ct); _logger.LogInformation("Found {Count} active traders to analyze", traderIds.Count); foreach (var id in traderIds) { try { await pnlEngine.RecalculateTraderPositionsAsync(id, ct); } catch (Exception ex) { _logger.LogError(ex, "Error recalculating positions/PnL for trader {TraderId}", id); } } _logger.LogInformation("Trader analytics update complete."); } }