204 lines
5.4 KiB
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
|
|
)
|
|
}
|
|
}
|