private bool LegalEntityPredicate(OvernightSwapReportType reportType, string legalEntity) { var filter = reportType == OvernightSwapReportType.Daily ? _dailyNotificationsSettings.Filter : _monthlyNotificationsSettings.Filter; return(string.IsNullOrWhiteSpace(filter.LegalEntityRegex) || (!string.IsNullOrWhiteSpace(legalEntity) && Regex.IsMatch(legalEntity, filter.LegalEntityRegex, RegexOptions.IgnoreCase))); }
private async Task SendNotifications(OvernightSwapReportType reportType, IEnumerable <PeriodicTradingNotification> notifications, IReadOnlyDictionary <string, string> emails) { var failedNotifications = new Dictionary <PeriodicTradingNotification, Exception>(); Exception anyException = null; foreach (var notification in notifications) { if (reportType == OvernightSwapReportType.Monthly || notification.Accounts.Sum(x => x.ClosedTrades.Count) > 0 || notification.Accounts.Sum(x => x.OpenPositions.Count) > 0 || notification.Accounts.Sum(x => x.PendingPositions.Count) > 0) { try { await _emailService.PrepareAndSendEmailAsync(emails[notification.ClientId], "Margin Trading - " + (reportType == OvernightSwapReportType.Daily ? "Daily" : "Monthly") + $" trading report for {notification.CurrentPeriod}", reportType == OvernightSwapReportType.Daily? "DailyTradingReport" : "MonthlyTradingReport", notification); await _log.WriteInfoAsync(nameof(NotificationGenerator), nameof(TradingReportService), nameof(PerformReporting), notification.GetLogData(), _systemClock.UtcNow.DateTime); } catch (Exception ex) { anyException = ex; failedNotifications.Add(notification, ex); } } } if (failedNotifications.Any()) { //TODO handle failed notifications await _log.WriteWarningAsync(nameof(NotificationGenerator), nameof(TradingReportService), nameof(PerformReporting), $"{failedNotifications.Count} notifications failed to be send.", anyException, _systemClock.UtcNow.DateTime); } }
public async Task PerformReporting(OvernightSwapReportType reportType) { var invocationTime = GetInvocationTime(reportType); var from = reportType == OvernightSwapReportType.Daily ? _systemClock.UtcNow.Date.AddDays(-1) : _systemClock.UtcNow.Date.AddMonths(-1); var to = new DateTime(_systemClock.UtcNow.Year, _systemClock.UtcNow.Month, _systemClock.UtcNow.Day, invocationTime.Hours, invocationTime.Minutes, invocationTime.Seconds); var reportPeriodIndex = reportType == OvernightSwapReportType.Daily ? from.DayOfWeek.ToString() : from.Month.ToString("MMMM"); await _log.WriteInfoAsync(nameof(TradingReportService), nameof(PerformReporting), $"Report invoked for {reportPeriodIndex}, period from {from:s} to {to:s}"); var(clientIds, accounts, closedTrades, openPositions, pendingPositions, accountTransactions) = await GetDataForNotifications(reportType, to, from); //prepare notification models var notifications = clientIds.Select(x => PrepareNotification(reportType, from, to, x, closedTrades, openPositions, pendingPositions, accounts, accountTransactions)).ToList(); //retrieve emails var emails = (await _clientAccountClient.GetClientsByIdsAsync(clientIds)) .ToDictionary(x => x.Id, x => x.Email); if (reportType == OvernightSwapReportType.Daily ? _dailyNotificationsSettings.EmailNotificationEnabled : _monthlyNotificationsSettings.EmailNotificationEnabled) { await SendNotifications(reportType, notifications, emails); } else { await _log.WriteInfoAsync(nameof(TradingReportService), nameof(PerformReporting), $"Email notifications are disabled for {reportType.ToString()} reports."); } }
private static PeriodicTradingNotification PrepareNotification(OvernightSwapReportType reportType, DateTime from, DateTime to, string clientId, IReadOnlyCollection <OrderHistory> closedTrades, IReadOnlyCollection <OrderHistory> openPositions, IEnumerable <OrderHistory> pendingPositions, IEnumerable <Account> accounts, IEnumerable <AccountHistory> accountTransactions) { bool TimeClause(DateTime t) => t > @from && t < to; var closedTradesFiltered = closedTrades.Where(x => TimeClause(x.CloseDate ?? DateTime.MaxValue)).ToList(); var orderInstruments = closedTradesFiltered.Concat(openPositions).ToDictionary(x => x.Id, x => x.Instrument); var closedTradesPnls = closedTradesFiltered.GroupBy(x => x.AccountId).ToDictionary(x => x.Key, x => x.Sum(ct => ct.PnL)); var floatingPnls = openPositions.GroupBy(x => x.AccountId).ToDictionary(x => x.Key, x => x.Sum(ct => ct.PnL)); var closedTradesByAccount = closedTradesFiltered.Where(x => x.ClientId == clientId) .GroupBy(x => x.AccountId).ToDictionary(x => x.Key, x => x.OrderByDescending(z => z.CloseDate).ToList()); var openPositionsByAccount = openPositions.Where(x => x.ClientId == clientId) .GroupBy(x => x.AccountId).ToDictionary(x => x.Key, x => x.OrderByDescending(z => z.OpenDate).ToList()); var pendingPositionsByAccount = pendingPositions.Where(x => x.ClientId == clientId) .GroupBy(x => x.AccountId).ToDictionary(x => x.Key, x => x.OrderByDescending(z => z.CreateDate).ToList()); var filteredAccounts = accounts.Where(x => x.ClientId == clientId) .Select(x => { x.AccountTransactions = accountTransactions.Where(at => TimeClause(at.Date) && at.ClientId == clientId && at.AccountId == x.Id) .Select(at => { at.Comment = !string.IsNullOrEmpty(at.OrderId) && orderInstruments.TryGetValue(at.OrderId, out var instrument) ? $"position id: {at.OrderId} ({instrument})" : at.Comment; return(at); }) .OrderByDescending(at => at.Date).ToList(); if (closedTradesPnls.TryGetValue(x.Id, out var closedTradesPnl)) { x.ClosedTradesPnl = closedTradesPnl; } if (floatingPnls.TryGetValue(x.Id, out var floatingPnl)) { x.FloatingPnl = floatingPnl; } x.CashTransactions = x.AccountTransactions.Sum(at => at.Amount); x.Equity = x.Balance + x.FloatingPnl; x.ChangeInBalance = x.ClosedTradesPnl + x.CashTransactions; x.AvailableMargin = x.Equity - x.MarginRequirements; x.ClosedTrades = closedTradesByAccount.TryGetValue(x.Id, out var extractedClosed) ? extractedClosed : new List <OrderHistory>(); x.OpenPositions = openPositionsByAccount.TryGetValue(x.Id, out var extractedOpened) ? extractedOpened : new List <OrderHistory>(); x.PendingPositions = pendingPositionsByAccount.TryGetValue(x.Id, out var extractedPending) ? extractedPending : new List <OrderHistory>(); return(x); }) .OrderByDescending(x => x.Balance).ThenBy(x => x.BaseAssetId).ToList(); return(new PeriodicTradingNotification { CurrentPeriod = reportType == OvernightSwapReportType.Daily ? from.ToString("dd.MM.yyyy") : from.ToString("MM.yyyy"), From = $"{@from:dd.MM.yyyy} 00:00", To = $"{to.AddMinutes(-1):dd.MM.yyyy HH:mm}", ClientId = clientId, Accounts = filteredAccounts, ReportType = reportType, });