InvestmentTrackerApp/PortfolioJournal/Models/InvestmentMetrics.swift

204 lines
5.4 KiB
Swift

import Foundation
struct InvestmentMetrics {
// Basic Metrics
let totalValue: Decimal
let totalContributions: Decimal
let absoluteReturn: Decimal
let percentageReturn: Decimal
// Advanced Metrics
let cagr: Double // Compound Annual Growth Rate
let twr: Double // Time-Weighted Return
let volatility: Double // Standard deviation annualized
let maxDrawdown: Double // Maximum peak-to-trough decline
let sharpeRatio: Double // Risk-adjusted return
let bestMonth: MonthlyReturn?
let worstMonth: MonthlyReturn?
let winRate: Double // Percentage of positive months
let averageMonthlyReturn: Double
// Time period
let startDate: Date?
let endDate: Date?
let totalMonths: Int
struct MonthlyReturn: Identifiable {
let id = UUID()
let date: Date
let returnPercentage: Double
var formattedDate: String {
let formatter = DateFormatter()
formatter.dateFormat = "MMM yyyy"
return formatter.string(from: date)
}
var formattedReturn: String {
String(format: "%.1f%%", returnPercentage)
}
}
}
// MARK: - Formatting Helpers
extension InvestmentMetrics {
var formattedTotalValue: String {
formatCurrency(totalValue)
}
var formattedAbsoluteReturn: String {
let prefix = absoluteReturn >= 0 ? "+" : ""
return prefix + formatCurrency(absoluteReturn)
}
var formattedPercentageReturn: String {
let prefix = percentageReturn >= 0 ? "+" : ""
return String(format: "\(prefix)%.2f%%", NSDecimalNumber(decimal: percentageReturn).doubleValue)
}
var formattedCAGR: String {
String(format: "%.2f%%", cagr)
}
var formattedTWR: String {
String(format: "%.2f%%", twr)
}
var formattedVolatility: String {
String(format: "%.2f%%", volatility)
}
var formattedMaxDrawdown: String {
String(format: "%.2f%%", maxDrawdown)
}
var formattedSharpeRatio: String {
String(format: "%.2f", sharpeRatio)
}
var formattedWinRate: String {
String(format: "%.0f%%", winRate)
}
var formattedAverageMonthlyReturn: String {
let prefix = averageMonthlyReturn >= 0 ? "+" : ""
return String(format: "\(prefix)%.2f%%", averageMonthlyReturn)
}
private func formatCurrency(_ value: Decimal) -> String {
CurrencyFormatter.format(value, style: .currency, maximumFractionDigits: 2)
}
}
// MARK: - Empty State
extension InvestmentMetrics {
static var empty: InvestmentMetrics {
InvestmentMetrics(
totalValue: 0,
totalContributions: 0,
absoluteReturn: 0,
percentageReturn: 0,
cagr: 0,
twr: 0,
volatility: 0,
maxDrawdown: 0,
sharpeRatio: 0,
bestMonth: nil,
worstMonth: nil,
winRate: 0,
averageMonthlyReturn: 0,
startDate: nil,
endDate: nil,
totalMonths: 0
)
}
}
// MARK: - Category Metrics
struct CategoryMetrics: Identifiable {
let id: UUID
let categoryName: String
let colorHex: String
let icon: String
let totalValue: Decimal
let percentageOfPortfolio: Double
let metrics: InvestmentMetrics
var formattedTotalValue: String {
CurrencyFormatter.format(totalValue, style: .currency, maximumFractionDigits: 0)
}
var formattedPercentage: String {
String(format: "%.1f%%", percentageOfPortfolio)
}
}
// MARK: - Portfolio Summary
struct PortfolioSummary {
let totalValue: Decimal
let totalContributions: Decimal
let dayChange: Decimal
let dayChangePercentage: Double
let weekChange: Decimal
let weekChangePercentage: Double
let monthChange: Decimal
let monthChangePercentage: Double
let yearChange: Decimal
let yearChangePercentage: Double
let allTimeReturn: Decimal
let allTimeReturnPercentage: Double
let sourceCount: Int
let lastUpdated: Date?
var formattedTotalValue: String {
CurrencyFormatter.format(totalValue, style: .currency, maximumFractionDigits: 0)
}
var formattedDayChange: String {
formatChange(dayChange, dayChangePercentage)
}
var formattedMonthChange: String {
formatChange(monthChange, monthChangePercentage)
}
var formattedYearChange: String {
formatChange(yearChange, yearChangePercentage)
}
var formattedAllTimeReturn: String {
formatChange(allTimeReturn, allTimeReturnPercentage)
}
private func formatChange(_ absolute: Decimal, _ percentage: Double) -> String {
let prefix = absolute >= 0 ? "+" : ""
let absString = CurrencyFormatter.format(absolute, style: .currency, maximumFractionDigits: 0)
let pctString = String(format: "%.2f%%", percentage)
return "\(prefix)\(absString) (\(prefix)\(pctString))"
}
static var empty: PortfolioSummary {
PortfolioSummary(
totalValue: 0,
totalContributions: 0,
dayChange: 0,
dayChangePercentage: 0,
weekChange: 0,
weekChangePercentage: 0,
monthChange: 0,
monthChangePercentage: 0,
yearChange: 0,
yearChangePercentage: 0,
allTimeReturn: 0,
allTimeReturnPercentage: 0,
sourceCount: 0,
lastUpdated: nil
)
}
}