package templates import ( "fmt" "portfolio-tracker/internal/model" "portfolio-tracker/internal/util" "time" ) // calculateTotalInvested calculates the total amount invested (buy transactions) func calculateTotalInvested(activities []model.Activity) float64 { var total float64 = 0 for _, activity := range activities { if activity.Type == model.Buy { total += activity.Amount * activity.Price } } return total } // calculateTotalInvestedInBaseCurrency calculates total invested converted to portfolio base currency using historical rates func calculateTotalInvestedInBaseCurrency(activities []model.Activity, baseCurrency string) float64 { var total float64 = 0 for _, activity := range activities { if activity.Type == model.Buy { activityTotal := activity.Amount * activity.Price if activity.Currency == baseCurrency { total += activityTotal } else { // Use historical exchange rate for the transaction date convertedTotal, err := util.ConvertCurrencyHistorical(activityTotal, activity.Currency, baseCurrency, activity.Date) if err != nil { // If historical conversion fails, try current rate as fallback fallbackTotal, fallbackErr := util.ConvertCurrency(activityTotal, activity.Currency, baseCurrency) if fallbackErr != nil { // If all conversions fail, add original amount (last resort) total += activityTotal } else { total += fallbackTotal } } else { total += convertedTotal } } } } return total } // getLastBuyDate returns the date of the last buy transaction func getLastBuyDate(activities []model.Activity) string { var lastBuy time.Time for _, activity := range activities { if activity.Type == model.Buy && activity.Date.After(lastBuy) { lastBuy = activity.Date } } if lastBuy.IsZero() { return "Keine Käufe" } return lastBuy.Format("02.01.2006") } // convertActivityPrice converts activity price to portfolio base currency using historical rate func convertActivityPrice(activity model.Activity, portfolioBaseCurrency string) (float64, string, error) { if activity.Currency == portfolioBaseCurrency { return activity.Price, "", nil // No conversion needed } convertedPrice, err := util.ConvertCurrencyHistorical(activity.Price, activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return 0, "", err } return convertedPrice, portfolioBaseCurrency, nil } // formatActivityPrice formats activity price with conversion if needed using historical rates func formatActivityPrice(activity model.Activity, portfolioBaseCurrency string) string { if activity.Currency == portfolioBaseCurrency { return fmt.Sprintf("%.2f %s", activity.Price, activity.Currency) } convertedPrice, err := util.ConvertCurrencyHistorical(activity.Price, activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return fmt.Sprintf("%.2f %s (conv. error)", activity.Price, activity.Currency) } return fmt.Sprintf("%.2f %s (~%.2f %s)", activity.Price, activity.Currency, convertedPrice, portfolioBaseCurrency) } // formatActivityTotal formats activity total with conversion if needed using historical rates func formatActivityTotal(activity model.Activity, portfolioBaseCurrency string) string { total := activity.Amount * activity.Price if activity.Currency == portfolioBaseCurrency { return fmt.Sprintf("%.2f %s", total, activity.Currency) } convertedTotal, err := util.ConvertCurrencyHistorical(total, activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return fmt.Sprintf("%.2f %s (conv. error)", total, activity.Currency) } return fmt.Sprintf("%.2f %s (~%.2f %s)", total, activity.Currency, convertedTotal, portfolioBaseCurrency) } // getConvertedPrice returns the converted price using historical exchange rate or original if conversion fails func getConvertedPrice(activity model.Activity, portfolioBaseCurrency string) string { if activity.Currency == portfolioBaseCurrency { return "" } // Use historical exchange rate for the transaction date convertedPrice, isHistorical, err := util.GetExchangeRateWithFallback(activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return "Conv. Error" } finalPrice := activity.Price * convertedPrice // Add indicator if fallback (current) rate was used instead of historical if !isHistorical { return fmt.Sprintf("%.2f %s*", finalPrice, portfolioBaseCurrency) } return fmt.Sprintf("%.2f %s", finalPrice, portfolioBaseCurrency) } // getConvertedTotal returns the converted total using historical exchange rate or original if conversion fails func getConvertedTotal(activity model.Activity, portfolioBaseCurrency string) string { if activity.Currency == portfolioBaseCurrency { return "" } total := activity.Amount * activity.Price // Use historical exchange rate for the transaction date convertedTotal, err := util.ConvertCurrencyHistorical(total, activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return "Conv. Error" } return fmt.Sprintf("%.2f %s", convertedTotal, portfolioBaseCurrency) } // getConvertedTotalWithFallbackInfo returns the converted total with information about rate source func getConvertedTotalWithFallbackInfo(activity model.Activity, portfolioBaseCurrency string) string { if activity.Currency == portfolioBaseCurrency { return "" } total := activity.Amount * activity.Price // Use historical exchange rate with fallback information rate, isHistorical, err := util.GetExchangeRateWithFallback(activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return "Conv. Error" } convertedTotal := total * rate // Add indicator if fallback (current) rate was used instead of historical if !isHistorical { return fmt.Sprintf("%.2f %s*", convertedTotal, portfolioBaseCurrency) } return fmt.Sprintf("%.2f %s", convertedTotal, portfolioBaseCurrency) } // formatTotalInvestedWithConversion formats total invested with currency info and conversion indicators func formatTotalInvestedWithConversion(activities []model.Activity, baseCurrency string) string { convertedTotal := calculateTotalInvestedInBaseCurrency(activities, baseCurrency) // Check if any activities have different currencies hasDifferentCurrencies := false hasHistoricalConversions := false for _, activity := range activities { if activity.Type == model.Buy && activity.Currency != baseCurrency { hasDifferentCurrencies = true // Check if we can get historical rate for this transaction _, isHistorical, err := util.GetExchangeRateWithFallback(activity.Currency, baseCurrency, activity.Date) if err == nil && isHistorical { hasHistoricalConversions = true } break } } if !hasDifferentCurrencies { return fmt.Sprintf("%.2f %s", convertedTotal, baseCurrency) } if hasHistoricalConversions { return fmt.Sprintf("%.2f %s (historical rates)", convertedTotal, baseCurrency) } return fmt.Sprintf("%.2f %s (converted)", convertedTotal, baseCurrency) } // calculateTotalInvestedByDate calculates total invested up to a specific date func calculateTotalInvestedByDate(activities []model.Activity, baseCurrency string, endDate time.Time) float64 { var total float64 = 0 for _, activity := range activities { if activity.Type == model.Buy && activity.Date.Before(endDate.AddDate(0, 0, 1)) { // Include transactions on endDate activityTotal := activity.Amount * activity.Price if activity.Currency == baseCurrency { total += activityTotal } else { // Use historical exchange rate for the transaction date convertedTotal, err := util.ConvertCurrencyHistorical(activityTotal, activity.Currency, baseCurrency, activity.Date) if err != nil { // If historical conversion fails, try current rate as fallback fallbackTotal, fallbackErr := util.ConvertCurrency(activityTotal, activity.Currency, baseCurrency) if fallbackErr != nil { // If all conversions fail, add original amount (last resort) total += activityTotal } else { total += fallbackTotal } } else { total += convertedTotal } } } } return total } // getConversionInfo returns information about currency conversions used in the portfolio func getConversionInfo(activities []model.Activity, baseCurrency string) map[string]interface{} { info := map[string]interface{}{ "hasDifferentCurrencies": false, "historicalConversions": 0, "fallbackConversions": 0, "errorConversions": 0, "currencies": make(map[string]int), } currencies := make(map[string]int) historicalCount := 0 fallbackCount := 0 errorCount := 0 for _, activity := range activities { if activity.Type == model.Buy { currencies[activity.Currency]++ if activity.Currency != baseCurrency { info["hasDifferentCurrencies"] = true // Check conversion status _, isHistorical, err := util.GetExchangeRateWithFallback(activity.Currency, baseCurrency, activity.Date) if err != nil { errorCount++ } else if isHistorical { historicalCount++ } else { fallbackCount++ } } } } info["historicalConversions"] = historicalCount info["fallbackConversions"] = fallbackCount info["errorConversions"] = errorCount info["currencies"] = currencies return info } // formatCurrencyWithConversionNote formats currency amount with appropriate conversion notes func formatCurrencyWithConversionNote(amount float64, fromCurrency, toCurrency string, transactionDate time.Time) string { if fromCurrency == toCurrency { return util.FormatCurrencyWithSymbol(amount, toCurrency) } rate, isHistorical, err := util.GetExchangeRateWithFallback(fromCurrency, toCurrency, transactionDate) if err != nil { return fmt.Sprintf("%.2f %s (conv. error)", amount, fromCurrency) } convertedAmount := amount * rate if isHistorical { return fmt.Sprintf("%.2f %s (historical rate)", convertedAmount, toCurrency) } return fmt.Sprintf("%.2f %s (current rate)", convertedAmount, toCurrency) } // getActivityConversionStatus returns the conversion status for a specific activity func getActivityConversionStatus(activity model.Activity, portfolioBaseCurrency string) string { if activity.Currency == portfolioBaseCurrency { return "same_currency" } _, isHistorical, err := util.GetExchangeRateWithFallback(activity.Currency, portfolioBaseCurrency, activity.Date) if err != nil { return "error" } if isHistorical { return "historical" } return "fallback" }