added first implementation of position list
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user