initial version
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user