Base fixes and test harness
This commit is contained in:
@@ -195,7 +195,11 @@ class DashboardViewModel: ObservableObject {
|
||||
snapshots: allSnapshots
|
||||
)
|
||||
updateEvolutionData(from: completedSnapshots, categories: categories)
|
||||
latestPortfolioChange = calculateLatestChange(from: evolutionData)
|
||||
latestPortfolioChange = calculateLatestCheckInChange(
|
||||
sources: sources,
|
||||
snapshots: allSnapshots,
|
||||
fallback: evolutionData
|
||||
)
|
||||
|
||||
// Calculate portfolio forecast
|
||||
updatePortfolioForecast()
|
||||
@@ -360,7 +364,7 @@ class DashboardViewModel: ObservableObject {
|
||||
snapshots: [Snapshot]
|
||||
) -> [Snapshot] {
|
||||
guard let lastCompleted = MonthlyCheckInStore.latestCompletionDate()?.startOfMonth else {
|
||||
return []
|
||||
return snapshots
|
||||
}
|
||||
|
||||
let completedMonthsAfter = completedMonthKeys(
|
||||
@@ -371,6 +375,10 @@ class DashboardViewModel: ObservableObject {
|
||||
|
||||
return snapshots.filter { snapshot in
|
||||
let monthDate = snapshot.date.startOfMonth
|
||||
if let completionDate = MonthlyCheckInStore.completionDate(for: monthDate),
|
||||
snapshot.date > completionDate {
|
||||
return false
|
||||
}
|
||||
if monthDate <= lastCompleted {
|
||||
return true
|
||||
}
|
||||
@@ -382,7 +390,7 @@ class DashboardViewModel: ObservableObject {
|
||||
|
||||
private func calculateLatestChange(from data: [(date: Date, value: Decimal)]) -> PortfolioChange {
|
||||
guard data.count >= 2 else {
|
||||
return PortfolioChange(absolute: 0, percentage: 0, label: "since last update")
|
||||
return PortfolioChange(absolute: 0, percentage: 0, label: "since last check-in")
|
||||
}
|
||||
let last = data[data.count - 1]
|
||||
let previous = data[data.count - 2]
|
||||
@@ -390,7 +398,58 @@ class DashboardViewModel: ObservableObject {
|
||||
let percentage = previous.value > 0
|
||||
? NSDecimalNumber(decimal: absolute / previous.value).doubleValue * 100
|
||||
: 0
|
||||
return PortfolioChange(absolute: absolute, percentage: percentage, label: "since last update")
|
||||
return PortfolioChange(absolute: absolute, percentage: percentage, label: "since last check-in")
|
||||
}
|
||||
|
||||
private func calculateLatestCheckInChange(
|
||||
sources: [InvestmentSource],
|
||||
snapshots: [Snapshot],
|
||||
fallback: [(date: Date, value: Decimal)]
|
||||
) -> PortfolioChange {
|
||||
let completedMonths = MonthlyCheckInStore.allEntries()
|
||||
.compactMap { entry -> Date? in
|
||||
entry.entry.completionDate != nil ? entry.date.startOfMonth : nil
|
||||
}
|
||||
.sorted()
|
||||
|
||||
guard completedMonths.count >= 2 else {
|
||||
return calculateLatestChange(from: fallback)
|
||||
}
|
||||
|
||||
let lastMonth = completedMonths[completedMonths.count - 1]
|
||||
let previousMonth = completedMonths[completedMonths.count - 2]
|
||||
let lastCompletion = MonthlyCheckInStore.completionDate(for: lastMonth) ?? lastMonth.endOfMonth
|
||||
let previousCompletion = MonthlyCheckInStore.completionDate(for: previousMonth) ?? previousMonth.endOfMonth
|
||||
|
||||
let lastValue = totalPortfolioValue(asOf: lastCompletion, sources: sources, snapshots: snapshots)
|
||||
let previousValue = totalPortfolioValue(asOf: previousCompletion, sources: sources, snapshots: snapshots)
|
||||
let absolute = lastValue - previousValue
|
||||
let percentage = previousValue > 0
|
||||
? NSDecimalNumber(decimal: absolute / previousValue).doubleValue * 100
|
||||
: 0
|
||||
|
||||
return PortfolioChange(absolute: absolute, percentage: percentage, label: "since last check-in")
|
||||
}
|
||||
|
||||
private func totalPortfolioValue(
|
||||
asOf date: Date,
|
||||
sources: [InvestmentSource],
|
||||
snapshots: [Snapshot]
|
||||
) -> Decimal {
|
||||
let snapshotsBySource = Dictionary(grouping: snapshots) { $0.source?.id }
|
||||
var total = Decimal.zero
|
||||
|
||||
for source in sources {
|
||||
let sourceId = source.id
|
||||
guard let sourceSnapshots = snapshotsBySource[sourceId] else { continue }
|
||||
if let latest = sourceSnapshots
|
||||
.filter({ $0.date <= date })
|
||||
.max(by: { $0.date < $1.date }) {
|
||||
total += latest.decimalValue
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
private func updatePortfolioForecast() {
|
||||
|
||||
Reference in New Issue
Block a user