Skip to content

Commit 5a0eb6b

Browse files
authored
Fixing performance computing (#6)
1 parent 75edc99 commit 5a0eb6b

File tree

5 files changed

+47
-17
lines changed

5 files changed

+47
-17
lines changed

Sources/Grodt/BusinessLogic/PortfolioPerformance/PortfolioPerformanceCalculator.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22

33
protocol PortfolioPerformanceCalculating {
4-
func performance(of portfolio: Portfolio, on date: YearMonthDayDate) async throws -> DatedPortfolioPerformance
4+
func performance(of portfolio: Portfolio, on date: YearMonthDayDate, priceCache: inout [String: Decimal]) async throws -> DatedPortfolioPerformance
55
}
66

77
class PortfolioPerformanceCalculator: PortfolioPerformanceCalculating {
@@ -11,15 +11,28 @@ class PortfolioPerformanceCalculator: PortfolioPerformanceCalculating {
1111
self.priceService = priceService
1212
}
1313

14-
func performance(of portfolio: Portfolio, on date: YearMonthDayDate) async throws -> DatedPortfolioPerformance {
14+
func performance(
15+
of portfolio: Portfolio,
16+
on date: YearMonthDayDate,
17+
priceCache: inout [String: Decimal]
18+
) async throws -> DatedPortfolioPerformance {
1519
let transactionsUntilDate = portfolio.transactions.filter { YearMonthDayDate($0.purchaseDate) <= date }
1620

1721
let financialsForDate = Financials()
22+
1823
for transaction in transactionsUntilDate {
1924
let inAmount = transaction.numberOfShares * transaction.pricePerShareAtPurchase + transaction.fees
2025
await financialsForDate.addMoneyIn(inAmount)
2126

22-
let value = try await transaction.numberOfShares * self.priceService.price(for: transaction.ticker, on: date)
27+
let price: Decimal
28+
if let cachedPrice = priceCache[transaction.ticker] {
29+
price = cachedPrice
30+
} else {
31+
price = try await self.priceService.price(for: transaction.ticker, on: date)
32+
priceCache[transaction.ticker] = price
33+
}
34+
35+
let value = transaction.numberOfShares * price
2336
await financialsForDate.addValue(value)
2437
}
2538

Sources/Grodt/BusinessLogic/PortfolioPerformance/PortfolioPerformanceUpdater.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,20 @@ class PortfolioPerformanceUpdater: PortfolioHistoricalPerformanceUpdater {
3333
}
3434

3535
func updatePerformanceOfAllPortfolios() async throws {
36-
// Update historical prices for all tickers
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
3742
let allTickers = try await tickerRepository.allTickers()
3843
for ticker in allTickers {
3944
await rateLimiter.waitIfNeeded()
4045
_ = 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+
}
4150
}
4251

4352
// Update historical performance for all portfolios
@@ -53,10 +62,11 @@ class PortfolioPerformanceUpdater: PortfolioHistoricalPerformanceUpdater {
5362
func recalculateHistoricalPerformance(of portfolio: Portfolio) async throws {
5463
var datedPerformance = [DatedPortfolioPerformance]()
5564
guard let earliestTransaction = portfolio.earliestTransaction else { return }
56-
let dates = dateRangeUntilYesterday(from: earliestTransaction.purchaseDate)
65+
let dates = dateRangeUntilToday(from: earliestTransaction.purchaseDate)
66+
var priceCache = [String: Decimal]()
5767

5868
for date in dates {
59-
let performanceForDate = try await performanceCalculator.performance(of: portfolio, on: date)
69+
let performanceForDate = try await performanceCalculator.performance(of: portfolio, on: date, priceCache: &priceCache)
6070
datedPerformance.append(performanceForDate)
6171
}
6272

@@ -72,14 +82,14 @@ class PortfolioPerformanceUpdater: PortfolioHistoricalPerformanceUpdater {
7282
}
7383
}
7484

75-
private func dateRangeUntilYesterday(from startDate: Date) -> [YearMonthDayDate] {
85+
private func dateRangeUntilToday(from startDate: Date) -> [YearMonthDayDate] {
7686
var dates: [YearMonthDayDate] = []
7787
var currentDate = startDate
7888
let calendar = Calendar.current
79-
let yesterday = calendar.date(byAdding: .day, value: -1, to: Date())!
89+
let today = Date()
8090

8191

82-
while currentDate <= yesterday {
92+
while currentDate <= today {
8393
let ymdDate = YearMonthDayDate(currentDate)
8494
dates.append(ymdDate)
8595
currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)!

Sources/Grodt/BusinessLogic/PriceService.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ protocol PriceService {
55
func price(for ticker: String) async throws -> Decimal
66
func price(for ticker: String, on date: YearMonthDayDate) async throws -> Decimal
77
func fetchAndCreateHistoricalPrices(for ticker: String) async throws -> HistoricalQuote
8+
func fetchAndUpdatePrice(for outdatedQuote: Quote) async throws -> Decimal
89
}
910

1011
class CachedPriceService: PriceService {
@@ -93,15 +94,10 @@ class CachedPriceService: PriceService {
9394
let quotes = try await liveHistoricalPrices(for: ticker)
9495
let historicalQuote = HistoricalQuote(symbol: ticker, datedQuotes: quotes)
9596
try await quoteRepository.create(historicalQuote)
96-
97-
if let previousQuote = try await quoteRepository.historicalQuote(for: ticker) {
98-
try await quoteRepository.delete(previousQuote)
99-
}
100-
10197
return historicalQuote
10298
}
10399

104-
private func fetchAndUpdatePrice(for outdatedQuote: Quote) async throws -> Decimal {
100+
func fetchAndUpdatePrice(for outdatedQuote: Quote) async throws -> Decimal {
105101
let quote = try await latestQuote(for: outdatedQuote.symbol)
106102
outdatedQuote.lastUpdate = Date()
107103
try await quoteRepository.update(outdatedQuote)

Sources/Grodt/Persistency/Repositories/QuoteRepository.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ protocol QuoteRepository {
88

99
func update(_ quote: Quote) async throws
1010

11+
func allHistoricalQuote() async throws -> [HistoricalQuote]
12+
1113
func historicalQuote(for ticker: String) async throws -> HistoricalQuote?
1214

1315
func create(_ historicalQuote: HistoricalQuote) async throws
1416

1517
func delete(_ historicalQuote: HistoricalQuote) async throws
18+
19+
func update(_ historicalQuote: HistoricalQuote) async throws
1620
}
1721

1822
class PostgresQuoteRepository: QuoteRepository {
@@ -36,6 +40,11 @@ class PostgresQuoteRepository: QuoteRepository {
3640
try await quote.update(on: database)
3741
}
3842

43+
func allHistoricalQuote() async throws -> [HistoricalQuote] {
44+
return try await HistoricalQuote.query(on: database)
45+
.all()
46+
}
47+
3948
func historicalQuote(for ticker: String) async throws -> HistoricalQuote? {
4049
return try await HistoricalQuote.query(on: database)
4150
.filter(\HistoricalQuote.$symbol == ticker)
@@ -46,6 +55,10 @@ class PostgresQuoteRepository: QuoteRepository {
4655
try await historicalQuote.save(on: database)
4756
}
4857

58+
func update(_ historicalQuote: HistoricalQuote) async throws {
59+
try await historicalQuote.update(on: database)
60+
}
61+
4962
func delete(_ historicalQuote: HistoricalQuote) async throws {
5063
try await historicalQuote.delete(on: database)
5164
}

docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3.7'
2-
31
services:
42
db:
53
image: postgres

0 commit comments

Comments
 (0)