using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using PolyTrader.Core.Persistence; using PolyTrader.Modules.CopyTrading.Persistence; using PolyTraderSharp.Models; namespace PolyTraderSharp.Services { /// /// Hydriert den TradingState (Accounts, Demo-Positionen, Trader) EINMALIG beim App-Start. /// Registriert als erster IHostedService, damit die Hydration abgeschlossen ist, bevor die /// Trading-BackgroundServices (TraderMonitor, CopyTradingEngine, ...) ihre ExecuteAsync /// starten. Behebt den Startup-Race, bei dem Services gegen einen leeren State anliefen /// (früher lag diese Logik in frm_main.LoadDatabaseAndState und lief erst NACH AppHost.Start()). /// public class StartupHydrationService : IHostedService { private readonly TradingState _state; private readonly CopyTradingState _copyState; private readonly IAccountRepository _accountRepo; private readonly IPositionRepository _positionRepo; private readonly ICopyTradingAccountSettingsRepository _accountSettingsRepo; private readonly ITrackedTraderRepository _traderRepo; private readonly TerminalLogger _logger; public StartupHydrationService( TradingState state, CopyTradingState copyState, IAccountRepository accountRepo, IPositionRepository positionRepo, ICopyTradingAccountSettingsRepository accountSettingsRepo, ITrackedTraderRepository traderRepo, TerminalLogger logger) { _state = state; _copyState = copyState; _accountRepo = accountRepo; _positionRepo = positionRepo; _accountSettingsRepo = accountSettingsRepo; _traderRepo = traderRepo; _logger = logger; } public Task StartAsync(CancellationToken cancellationToken) { try { var accounts = _accountRepo.GetAll(); foreach (var acc in accounts) { if (acc.IsDemo) { foreach (var pos in _positionRepo.GetDemo(acc.AccountId)) { acc.OpenPositions.TryAdd(pos.TokenId, pos); } } _state.Accounts[acc.AccountId] = acc; } HydrateAccountSettings(accounts); foreach (var trd in _traderRepo.GetAll()) { _copyState.Traders[trd.Id] = trd; } _logger.Info($"Startup-Hydration abgeschlossen: {_state.Accounts.Count} Accounts, {_copyState.Traders.Count} Trader geladen."); } catch (System.Exception ex) { _logger.Error($"Startup-Hydration fehlgeschlagen: {ex.Message}"); } return Task.CompletedTask; } /// /// Lädt die Copytrading-Account-Settings in den State. Existiert für einen Account noch /// kein Eintrag, wird ein Default-Eintrag angelegt und persistiert. /// private void HydrateAccountSettings(List accounts) { var existing = _accountSettingsRepo.GetAll().ToDictionary(s => s.AccountId); int created = 0; foreach (var acc in accounts) { if (existing.TryGetValue(acc.AccountId, out var s)) { _copyState.AccountSettings[acc.AccountId] = s; continue; } var settings = new CopyTradingAccountSettings { AccountId = acc.AccountId }; _accountSettingsRepo.Upsert(settings); _copyState.AccountSettings[acc.AccountId] = settings; created++; } if (created > 0) _logger.Info($"Copytrading-Account-Settings: {created} Default-Eintrag/-Einträge angelegt."); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } }