package model import ( "fmt" "time" ) // Position represents a calculated position for a specific stock in a portfolio type Position struct { Stock string `json:"stock"` // Stock symbol/ISIN Shares float64 `json:"shares"` // Total shares held (can be negative if oversold) AverageCostPrice float64 `json:"average_cost_price"` // Average cost per share in portfolio base currency TotalCostBasis float64 `json:"total_cost_basis"` // Total amount invested in portfolio base currency CurrentPrice float64 `json:"current_price"` // Current market price per share in portfolio base currency CurrentValue float64 `json:"current_value"` // Current total value (shares * current_price) UnrealizedPL float64 `json:"unrealized_pl"` // Unrealized profit/loss (current_value - total_cost_basis) UnrealizedPLPct float64 `json:"unrealized_pl_pct"` // Unrealized profit/loss percentage Currency string `json:"currency"` // Stock's native currency BaseCurrency string `json:"base_currency"` // Portfolio's base currency LastUpdated time.Time `json:"last_updated"` // When position was last calculated // Dividend information TotalDividends float64 `json:"total_dividends"` // Total dividends received in base currency // Transaction summary TotalBought float64 `json:"total_bought"` // Total shares bought TotalSold float64 `json:"total_sold"` // Total shares sold RealizedPL float64 `json:"realized_pl"` // Realized profit/loss from sales } // PositionSummary represents aggregated portfolio position data type PositionSummary struct { TotalValue float64 `json:"total_value"` // Total portfolio value TotalCostBasis float64 `json:"total_cost_basis"` // Total amount invested TotalUnrealizedPL float64 `json:"total_unrealized_pl"` // Total unrealized profit/loss TotalRealizedPL float64 `json:"total_realized_pl"` // Total realized profit/loss TotalDividends float64 `json:"total_dividends"` // Total dividends received TotalReturn float64 `json:"total_return"` // Total return (realized + unrealized + dividends) TotalReturnPct float64 `json:"total_return_pct"` // Total return percentage Currency string `json:"currency"` // Portfolio base currency LastUpdated time.Time `json:"last_updated"` // When summary was calculated } // IsOpen returns true if the position has shares (positive or negative) func (p *Position) IsOpen() bool { return p.Shares != 0 } // IsLong returns true if the position has positive shares func (p *Position) IsLong() bool { return p.Shares > 0 } // IsShort returns true if the position has negative shares func (p *Position) IsShort() bool { return p.Shares < 0 } // GetDisplayValue returns the absolute value for display purposes func (p *Position) GetDisplayValue() float64 { if p.CurrentValue < 0 { return -p.CurrentValue } return p.CurrentValue } // GetUnrealizedPLColor returns CSS class for profit/loss color func (p *Position) GetUnrealizedPLColor() string { if p.UnrealizedPL > 0 { return "text-green" } else if p.UnrealizedPL < 0 { return "text-red" } return "text-muted" } // GetUnrealizedPLIcon returns icon for profit/loss func (p *Position) GetUnrealizedPLIcon() string { if p.UnrealizedPL > 0 { return "trending-up" } else if p.UnrealizedPL < 0 { return "trending-down" } return "minus" } // FormatCurrency formats a value with currency symbol func (p *Position) FormatCurrency(value float64) string { switch p.BaseCurrency { case "EUR": return fmt.Sprintf("€%.2f", value) case "USD": return fmt.Sprintf("$%.2f", value) case "GBP": return fmt.Sprintf("£%.2f", value) case "CHF": return fmt.Sprintf("%.2f CHF", value) default: return fmt.Sprintf("%.2f %s", value, p.BaseCurrency) } } // FormatShares formats share count with appropriate decimal places func (p *Position) FormatShares() string { if p.Shares == float64(int(p.Shares)) { return fmt.Sprintf("%.0f", p.Shares) } return fmt.Sprintf("%.4f", p.Shares) } // FormatPercentage formats percentage with sign and color func (p *Position) FormatPercentage() string { if p.UnrealizedPLPct > 0 { return fmt.Sprintf("+%.2f%%", p.UnrealizedPLPct) } return fmt.Sprintf("%.2f%%", p.UnrealizedPLPct) }