11import Foundation
22
33protocol PortfolioHistoricalPerformanceUpdater {
4- func recalculateHistoricalPerformance ( of portfolio: Portfolio ) async throws
4+ func recalculatePerformance ( of portfolio: Portfolio ) async throws
55 func updatePerformanceOfAllPortfolios( ) async throws
66}
77
88class PortfolioPerformanceUpdater: PortfolioHistoricalPerformanceUpdater {
99 private let userRepository : UserRepository
1010 private let portfolioRepository : PortfolioRepository
1111 private let tickerRepository : TickerRepository
12- private let quoteRepository : QuoteRepository
12+ private let quoteCache : QuoteCache
1313 private let priceService : PriceService
1414 private let performanceCalculator : PortfolioPerformanceCalculating
15- private let dataMapper : PortfolioDTOMapper
1615
1716 private let rateLimiter = RateLimiter ( maxRequestsPerMinute: 5 )
1817
1918 init ( userRepository: UserRepository ,
2019 portfolioRepository: PortfolioRepository ,
2120 tickerRepository: TickerRepository ,
22- quoteRepository : QuoteRepository ,
21+ quoteCache : QuoteCache ,
2322 priceService: PriceService ,
24- performanceCalculator: PortfolioPerformanceCalculating ,
25- dataMapper: PortfolioDTOMapper ) {
23+ performanceCalculator: PortfolioPerformanceCalculating ) {
2624 self . userRepository = userRepository
2725 self . portfolioRepository = portfolioRepository
2826 self . tickerRepository = tickerRepository
29- self . quoteRepository = quoteRepository
27+ self . quoteCache = quoteCache
3028 self . priceService = priceService
3129 self . performanceCalculator = performanceCalculator
32- self . dataMapper = dataMapper
3330 }
3431
3532 func updatePerformanceOfAllPortfolios( ) async throws {
36- // Remove all historrical prices
37- for quote in try await quoteRepository. allHistoricalQuote ( ) {
38- try await quoteRepository. delete ( quote)
39- }
40-
41- // Update historical prices and latest prices for all tickers
42- let allTickers = try await tickerRepository. allTickers ( )
43- for ticker in allTickers {
44- await rateLimiter. waitIfNeeded ( )
45- _ = try await priceService. fetchAndCreateHistoricalPrices ( for: ticker. symbol)
46- await rateLimiter. waitIfNeeded ( )
47- if let quote = try await quoteRepository. quote ( for: ticker. symbol) {
48- _ = try await priceService. fetchAndUpdatePrice ( for: quote)
49- }
50- }
51-
52- // Update historical performance for all portfolios
53- let users = try await userRepository. allUsers ( )
54- for user in users {
55- let allPortfolios = try await portfolioRepository. allPortfolios ( for: user. id!)
56- for portfolio in allPortfolios {
57- try await recalculateHistoricalPerformance ( of: portfolio)
58- }
59- }
33+ try await updateAllTickerPrices ( )
34+ try await updateHistotrycalPerformances ( )
6035 }
6136
62- func recalculateHistoricalPerformance ( of portfolio: Portfolio) async throws {
37+ func recalculatePerformance ( of portfolio: Portfolio ) async throws {
6338 var datedPerformance = [ DatedPortfolioPerformance] ( )
64- guard let earliestTransaction = portfolio. earliestTransaction else { return }
65- let dates = dateRangeUntilToday ( from: earliestTransaction. purchaseDate)
66- var priceCache = [ String: Decimal] ( )
67-
39+ let startDate : Date = {
40+ guard let earliestTransaction = portfolio. earliestTransaction else { return Date ( ) }
41+ return earliestTransaction. purchaseDate
42+ } ( )
43+
44+ let dictCache = DictionaryInMemoryTickerPriceCache ( )
45+ for transaction in portfolio . transactions {
46+ if let storedQuotes = try await quoteCache. historicalQuote ( for: transaction. ticker) {
47+ storedQuotes. datedQuotes. forEach { datedQuote in
48+ dictCache. setPrice ( datedQuote. price, for: transaction. ticker, on: datedQuote. date)
49+ }
50+ }
51+ }
52+
53+ let dates = dateRangeUntilToday ( from: startDate)
6854 for date in dates {
69- let performanceForDate = try await performanceCalculator. performance ( of: portfolio, on: date, priceCache: & priceCache)
55+ let performanceForDate = try await performanceCalculator. performance ( of: portfolio,
56+ on: date,
57+ using: dictCache)
7058 datedPerformance. append ( performanceForDate)
7159 }
7260
@@ -81,6 +69,33 @@ class PortfolioPerformanceUpdater: PortfolioHistoricalPerformanceUpdater {
8169 try await portfolioRepository. createHistoricalPerformance ( historicalPerformance)
8270 }
8371 }
72+
73+ private func updateAllTickerPrices( ) async throws{
74+ let allTickers = try await tickerRepository. allTickers ( )
75+ for ticker in allTickers {
76+ try await clearCache ( for: ticker. symbol)
77+
78+ await rateLimiter. waitIfNeeded ( )
79+ _ = try await priceService. historicalPrice ( for: ticker. symbol)
80+ await rateLimiter. waitIfNeeded ( )
81+ _ = try await priceService. price ( for: ticker. symbol)
82+ }
83+ }
84+
85+ private func updateHistotrycalPerformances( ) async throws {
86+ let users = try await userRepository. allUsers ( )
87+ for user in users {
88+ let allPortfolios = try await portfolioRepository. allPortfolios ( for: user. id!)
89+ for portfolio in allPortfolios {
90+ try await recalculatePerformance ( of: portfolio)
91+ }
92+ }
93+ }
94+
95+ private func clearCache( for ticker: String) async throws {
96+ try await quoteCache. clearHistoricalQuote ( for: ticker)
97+ try await quoteCache. clearQuote ( for: ticker)
98+ }
8499
85100 private func dateRangeUntilToday( from startDate: Date) - > [ YearMonthDayDate] {
86101 var dates : [ YearMonthDayDate ] = [ ]
0 commit comments