Documentation
¶
Overview ¶
Package portfolio provides a comprehensive set of functions and types for managing a personal financial portfolio. It is designed to be local-first, auditable, and extensible, ensuring users have full control and transparency over their financial data.
The core functionalities include:
- Ledger Management: Recording and tracking all financial transactions (e.g., buys, sells, dividends, deposits, withdrawals, currency conversions, and accruals) in an immutable, chronological record.
- Market Data Integration: Storing and utilizing security information and historical prices to provide accurate valuations and performance metrics.
- Accounting System: A stateless engine that processes ledger and market data to generate insights such as holdings, gains, and performance summaries.
- Security Identification: A robust system for linking user-defined tickers to globally unique security identifiers.
- Data Persistence: Handling the encoding and decoding of financial data to and from human-readable, version-controllable formats (e.g., JSONL).
This package serves as the foundational logic for the `pcs` command-line tool, ensuring that all operations are consistent and based on a single source of truth.
In a new file portfolio/json_writer.go
Index ¶
- Constants
- func AcceptAll(Transaction) bool
- func BySecurity(ticker string) func(Transaction) bool
- func ByUpdatePrice() func(Transaction) bool
- func EncodeLedger(w io.Writer, ledger *Ledger) error
- func EncodeTransaction(w io.Writer, tx Transaction) error
- func SaveLedger(path string, ledger *Ledger) error
- func ValidateCurrency(code string) error
- func ValidateISIN(isin string) error
- func ValidateMIC(mic string) error
- type Accrue
- func (t *Accrue) Currency() string
- func (t Accrue) Equal(other Transaction) bool
- func (t Accrue) MarshalJSON() ([]byte, error)
- func (t Accrue) Rationale() string
- func (t *Accrue) UnmarshalJSON(data []byte) error
- func (t Accrue) Validate(ledger *Ledger) (Transaction, error)
- func (t Accrue) What() CommandType
- func (t Accrue) When() Date
- type Buy
- type CommandType
- type Convert
- func (t Convert) Equal(other Transaction) bool
- func (t *Convert) FromCurrency() string
- func (t Convert) MarshalJSON() ([]byte, error)
- func (t Convert) Rationale() string
- func (t *Convert) ToCurrency() string
- func (t *Convert) UnmarshalJSON(data []byte) error
- func (t Convert) Validate(ledger *Ledger) (Transaction, error)
- func (t Convert) What() CommandType
- func (t Convert) When() Date
- type CostBasisMethod
- type Date
- func (d Date) Add(i int) Date
- func (d Date) AddMonth(i int) Date
- func (d Date) After(x Date) bool
- func (d Date) Before(x Date) bool
- func (d Date) Compare(e Date) int
- func (d Date) Day() int
- func (d Date) DayString() string
- func (d Date) EndOf(period Period) Date
- func (d Date) Format(format string) string
- func (d Date) Full() string
- func (d Date) ISOWeek() (year, week int)
- func (d Date) IsToday() bool
- func (d Date) IsZero() bool
- func (d Date) MarshalJSON() ([]byte, error)
- func (d Date) Month() time.Month
- func (d Date) Quarter() int
- func (d Date) Range(period Period) Range
- func (d Date) StartOf(period Period) Date
- func (d Date) String() string
- func (d Date) Unix() int64
- func (d *Date) UnmarshalJSON(bytes []byte) error
- func (dd *Date) UnmarshalText(text []byte) error
- func (d Date) Weekday() time.Weekday
- func (d Date) Year() int
- type Declare
- type Deposit
- func (t Deposit) Currency() string
- func (t Deposit) Equal(other Transaction) bool
- func (t Deposit) MarshalJSON() ([]byte, error)
- func (t Deposit) Rationale() string
- func (t *Deposit) UnmarshalJSON(data []byte) error
- func (t Deposit) Validate(ledger *Ledger) (Transaction, error)
- func (t Deposit) What() CommandType
- func (t Deposit) When() Date
- type Dividend
- type DividendInfo
- type ID
- func (id ID) Base() string
- func (id ID) CurrencyPair() (base string, quote string, err error)
- func (id ID) ISIN() (isin string, err error)
- func (id ID) IsCurrencyPair() bool
- func (id ID) IsISIN() bool
- func (id ID) IsMSSI() bool
- func (id ID) MIC() string
- func (id ID) MSSI() (isin string, mic string, err error)
- func (id ID) Private() (private string, err error)
- func (id ID) Quote() string
- func (id ID) String() string
- type Init
- type Journal
- type Ledger
- func (l *Ledger) AllCounterpartyAccounts() iter.Seq[string]
- func (l *Ledger) AllSecurities() iter.Seq[Security]
- func (l *Ledger) Append(txs ...Transaction) error
- func (l *Ledger) ByCurrency(currency string) func(Transaction) bool
- func (l *Ledger) CashBalance(currency string, on Date) Money
- func (l *Ledger) CounterPartyCurrency(account string) (cur string, exists bool)
- func (l *Ledger) CounterpartyAccountBalance(account string, on Date) Money
- func (ledger *Ledger) Currencies() iter.Seq[string]
- func (ledger *Ledger) Currency() string
- func (l *Ledger) Fmt() (*Ledger, error)
- func (l *Ledger) GenerateLog(r Range, period Period) ([]*Review, error)
- func (l *Ledger) GlobalInceptionDate() Date
- func (l *Ledger) HeldSecuritiesInRange(period Range) iter.Seq[Security]
- func (l *Ledger) InceptionDate(security string) Date
- func (l *Ledger) Journal() *Journal
- func (l *Ledger) LastKnownMarketDataDate(security string) Date
- func (ledger *Ledger) LastOperationDate(s string) Date
- func (ledger *Ledger) Name() string
- func (l *Ledger) NewReview(period Range) *Review
- func (l *Ledger) NewSnapshot(on Date) *Snapshot
- func (l *Ledger) NewestTransactionDate() Date
- func (l *Ledger) OldestTransactionDate() Date
- func (l *Ledger) Position(on Date, ticker string) Quantity
- func (l *Ledger) Security(ticker string) *Security
- func (l Ledger) Transactions(accepts ...func(Transaction) bool) iter.Seq2[int, Transaction]
- func (l *Ledger) UpdateIntraday() error
- func (l *Ledger) UpdateMarketData(txs ...Transaction) (MarketDataUpdate, error)
- func (l *Ledger) Validate(tx Transaction) (Transaction, error)
- type MarketDataUpdate
- func (m MarketDataUpdate) AddedDividends() int
- func (m MarketDataUpdate) AddedPrices() int
- func (m MarketDataUpdate) NewSplits() int
- func (m MarketDataUpdate) Total() int
- func (m MarketDataUpdate) UpdatedDividends() int
- func (m MarketDataUpdate) UpdatedPrices() int
- func (m MarketDataUpdate) UpdatedSplits() int
- type Money
- func (m Money) Add(n Money) Money
- func (m Money) AsFloat() float64deprecated
- func (m Money) Currency() string
- func (m Money) Div(n Quantity) Money
- func (m Money) DivPrice(n Money) Quantity
- func (m Money) Equal(n Money) bool
- func (m Money) GreaterThan(n Money) bool
- func (m Money) GreaterThanOrEqual(n Money) bool
- func (m Money) IsNegative() bool
- func (m Money) IsPositive() bool
- func (m Money) IsZero() bool
- func (m Money) LessThan(amount Money) bool
- func (m Money) LessThanOrEqual(n Money) bool
- func (m Money) MarshalJSON() ([]byte, error)
- func (m Money) Mul(n Quantity) Money
- func (m Money) Neg() Money
- func (m Money) SignedString() string
- func (m Money) String() string
- func (m Money) Sub(n Money) Money
- type Percent
- type Performance
- type Period
- type ProviderResponse
- type Quantity
- func (t Quantity) Add(p Quantity) Quantity
- func (t Quantity) Div(p Quantity) Quantity
- func (t Quantity) Equal(p Quantity) bool
- func (t Quantity) GreaterThan(p Quantity) bool
- func (t Quantity) IsNegative() bool
- func (t Quantity) IsPositive() bool
- func (t Quantity) IsZero() bool
- func (t Quantity) LessThan(quantity Quantity) bool
- func (t Quantity) MarshalJSON() ([]byte, error)
- func (t Quantity) Mul(p Quantity) Quantity
- func (q Quantity) String() string
- func (t Quantity) Sub(p Quantity) Quantity
- func (t *Quantity) UnmarshalJSON(decimalBytes []byte) error
- type Range
- type Review
- func (r *Review) AssetCostBasis(ticker string, method CostBasisMethod) Money
- func (r *Review) AssetDividends(ticker string) Money
- func (r *Review) AssetMarketGain(ticker string) Money
- func (r *Review) AssetNetTradingFlow(ticker string) Money
- func (r *Review) AssetRealizedGains(ticker string, method CostBasisMethod) Money
- func (r *Review) AssetTimeWeightedReturn(ticker string) Percent
- func (r *Review) AssetTotalReturn(ticker string) Money
- func (r *Review) CashChange() Money
- func (r *Review) CashFlow() Money
- func (r *Review) CounterpartyChange() Money
- func (r *Review) CurrencyTimeWeightedReturn(currency string) Percent
- func (r *Review) DividendReturn() Percent
- func (r *Review) Dividends() Money
- func (r *Review) End() *Snapshot
- func (r *Review) MarketGain() Money
- func (r *Review) Name() string
- func (r *Review) NetTradingFlow() Money
- func (r *Review) PortfolioChange() Money
- func (r *Review) Range() Range
- func (r *Review) RealizedGains(method CostBasisMethod) Money
- func (r *Review) Start() *Snapshot
- func (r *Review) TimeWeightedReturn() Percent
- func (r *Review) TotalCostBasis(method CostBasisMethod) Money
- func (r *Review) TotalMarketChange() Money
- func (r *Review) TotalReturn() Money
- func (r *Review) Transactions() []Transaction
- func (r *Review) UnrealizedGains(method CostBasisMethod) Money
- type Security
- type Sell
- type Snapshot
- func (s *Snapshot) Cash(currency string) Money
- func (s *Snapshot) CashFlow(currency string) Money
- func (s *Snapshot) Convert(amount Money) Money
- func (s *Snapshot) CostBasis(ticker string, method CostBasisMethod) Money
- func (s *Snapshot) Counterparties() iter.Seq[string]
- func (s *Snapshot) Counterparty(account string) Money
- func (s *Snapshot) Currencies() iter.Seq[string]
- func (s *Snapshot) Dividends(ticker string) Money
- func (s *Snapshot) ExchangeRate(currency string) Money
- func (s *Snapshot) LastMarketDataDate(ticker string) Date
- func (s *Snapshot) MarketValue(ticker string) Money
- func (s *Snapshot) Name() string
- func (s *Snapshot) NetTradingFlow(ticker string) Money
- func (s *Snapshot) On() Date
- func (s *Snapshot) Position(ticker string) Quantity
- func (s *Snapshot) Price(ticker string) Money
- func (s *Snapshot) RealizedGains(ticker string, method CostBasisMethod) Money
- func (s *Snapshot) ReportingCurrency() string
- func (s *Snapshot) Securities() iter.Seq[string]
- func (s *Snapshot) SecurityDetails(ticker string) (Security, bool)
- func (s *Snapshot) TotalCash() Money
- func (s *Snapshot) TotalCashFlow() Money
- func (s *Snapshot) TotalCostBasis(method CostBasisMethod) Money
- func (s *Snapshot) TotalCounterparty() Money
- func (s *Snapshot) TotalCounterpartyIn(currency string) Money
- func (s *Snapshot) TotalDividends() Money
- func (s *Snapshot) TotalMarket() Money
- func (s *Snapshot) TotalMarketIn(currency string) Money
- func (s *Snapshot) TotalNetTradingFlow() Money
- func (s *Snapshot) TotalPortfolio() Money
- func (s *Snapshot) TotalRealizedGains(method CostBasisMethod) Money
- func (s *Snapshot) TotalUnrealizedGains(method CostBasisMethod) Money
- func (s *Snapshot) UnrealizedGains(ticker string, method CostBasisMethod) Money
- func (s *Snapshot) VirtualAssetValue(ticker string) Money
- func (s *Snapshot) VirtualTotalValue() Money
- type Split
- type SplitInfo
- type Transaction
- type UpdatePrice
- func (t UpdatePrice) Equal(other Transaction) bool
- func (t UpdatePrice) MarshalJSON() ([]byte, error)
- func (t UpdatePrice) PricesIter() iter.Seq2[string, decimal.Decimal]
- func (t UpdatePrice) Rationale() string
- func (t *UpdatePrice) UnmarshalJSON(data []byte) error
- func (t UpdatePrice) Validate(ledger *Ledger) (Transaction, error)
- func (t UpdatePrice) What() CommandType
- func (t UpdatePrice) When() Date
- type Withdraw
- func (t *Withdraw) Currency() string
- func (t Withdraw) Equal(other Transaction) bool
- func (t Withdraw) MarshalJSON() ([]byte, error)
- func (t Withdraw) Rationale() string
- func (t *Withdraw) UnmarshalJSON(data []byte) error
- func (t Withdraw) Validate(ledger *Ledger) (Transaction, error)
- func (t Withdraw) What() CommandType
- func (t Withdraw) When() Date
Constants ¶
const DateFormat = "2006-01-02" // write date format
DateFormat is the format used to represent dates as strings in ISO-8601 format.
const DatetimeFormat = time.RFC3339
const Day = 24 * time.Hour
Variables ¶
This section is empty.
Functions ¶
func BySecurity ¶
func BySecurity(ticker string) func(Transaction) bool
BySecurity returns a predicate that filters transactions by security ticker.
func ByUpdatePrice ¶
func ByUpdatePrice() func(Transaction) bool
ByUpdatePrice returns a predicate that accept only UpdatePrice transactions
func EncodeLedger ¶
EncodeLedger reorders transactions by date and persists them to an io.Writer in JSONL format. The sort is stable, meaning transactions on the same day maintain their original relative order. It also ensures that the JSON keys within each transaction are sorted alphabetically for canonical output.
func EncodeTransaction ¶
func EncodeTransaction(w io.Writer, tx Transaction) error
EncodeTransaction marshals a single transaction to JSON and writes it to the writer, followed by a newline, in JSONL format.
func SaveLedger ¶
SaveLedger saves a single ledger to its corresponding file within the portfolio path. It uses the ledger's name to construct the file path (e.g., a ledger named "john/bnp" will be saved to "<path>/john/bnp.jsonl").
func ValidateCurrency ¶
ValidateCurrency checks if a string is a validly formatted ISO 4217 currency code.
func ValidateISIN ¶
ValidateISIN checks if a string is a validly formatted ISIN. It returns nil if valid, or a descriptive error if invalid.
func ValidateMIC ¶
ValidateMIC checks if a string conforms to the MIC (ISO 10383) format. It returns nil if valid, or a descriptive error if invalid. Note: This validates the format only, not whether the MIC is officially registered.
Types ¶
type Accrue ¶
type Accrue struct {
Counterparty string // Counterparty is the name of the entity with whom the accrual is made.
Amount Money // Amount is the value of the accrual. Positive for receivables, negative for payables.
Create bool // Create is true if this accrual creates a new counterparty account.
// contains filtered or unexported fields
}
Accrue represents a non-cash transaction that affects a counterparty account. Accrue represents a non-cash transaction that affects a counterparty account, such as a loan or an accrued expense/income.
func NewAccrue ¶
NewAccrue creates a new Accrue transaction. A positive amount indicates a receivable (an asset), meaning the counterparty owes the user money. A negative amount indicates a payable (a liability), meaning the user owes the counterparty money.
func NewCreatedAccrue ¶
NewCreatedAccrue creates a new Accrue transaction. A positive amount indicates a receivable (an asset), meaning the counterparty owes the user money. A negative amount indicates a payable (a liability), meaning the user owes the counterparty money.
func (Accrue) Equal ¶
func (t Accrue) Equal(other Transaction) bool
func (Accrue) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Accrue.
func (Accrue) Rationale ¶
func (t Accrue) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (*Accrue) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Accrue.
func (Accrue) Validate ¶
func (t Accrue) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Accrue transaction's fields.
func (Accrue) What ¶
func (t Accrue) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
type Buy ¶
type Buy struct {
Quantity Quantity // Quantity is the number of shares or units bought.
Amount Money // Amount is the total cost of the purchase.
// contains filtered or unexported fields
}
Buy represents a buy transaction. Buy represents a transaction where a quantity of a security is purchased for a specified amount.
func (Buy) Equal ¶
func (t Buy) Equal(other Transaction) bool
func (Buy) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Buy.
func (*Buy) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Buy. It handles the custom structure where amount and currency are separate fields.
func (Buy) Validate ¶
func (t Buy) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Buy transaction's fields. It ensures that the quantity and price are positive. It also verifies that there is enough cash in the corresponding currency account to cover the cost of the purchase on the transaction date. It now accepts a Ledger object.
type CommandType ¶
type CommandType string
CommandType is a typed string for identifying transaction commands.
const ( CmdInit CommandType = "init" CmdAccrue CommandType = "accrue" CmdBuy CommandType = "buy" CmdSell CommandType = "sell" CmdDividend CommandType = "dividend" CmdDeposit CommandType = "deposit" CmdWithdraw CommandType = "withdraw" CmdConvert CommandType = "convert" CmdDeclare CommandType = "declare" CmdUpdatePrice CommandType = "update-price" CmdSplit CommandType = "split" )
Command types used for identifying transactions.
type Convert ¶
Convert represents an internal currency conversion. Convert represents an internal currency conversion.
func NewConvert ¶
NewConvert creates a new Convert transaction.
func (Convert) Equal ¶
func (t Convert) Equal(other Transaction) bool
func (*Convert) FromCurrency ¶
func (Convert) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Convert.
func (Convert) Rationale ¶
func (t Convert) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (*Convert) ToCurrency ¶
func (*Convert) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Convert.
func (Convert) Validate ¶
func (t Convert) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Convert transaction's fields. It handles a "convert all" case if the from-amount is 0. It ensures both amounts are positive, currencies are valid, and there is sufficient cash in the source currency account to cover the conversion.
func (Convert) What ¶
func (t Convert) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
type CostBasisMethod ¶
type CostBasisMethod int
CostBasisMethod defines the method for calculating cost basis.
const ( // AverageCost calculates the cost basis by averaging the cost of all shares. AverageCost CostBasisMethod = iota // FIFO (First-In, First-Out) calculates the cost basis by assuming the first shares purchased are the first ones sold. FIFO )
func ParseCostBasisMethod ¶
func ParseCostBasisMethod(s string) (CostBasisMethod, error)
ParseCostBasisMethod parses a string into a CostBasisMethod.
func (CostBasisMethod) String ¶
func (m CostBasisMethod) String() string
type Date ¶
type Date struct {
// contains filtered or unexported fields
}
Date represents a date with day-level granularity.
func ParseDate ¶
ParseDate parses a Date from a string. It is lenient and accepts formats like "2025-7-1".
func (Date) Format ¶
Format returns a textual representation of the date value formatted according to the layout defined by the argument.
See the documentation for the [time.Format].
func (Date) MarshalJSON ¶
func (Date) Unix ¶
Unix returns t as a Unix time, the number of seconds elapsed since January 1, 1970 UTC.
func (*Date) UnmarshalJSON ¶
UnmarshalJSON implements the json specific way to unmarshall a date from a json string.
func (*Date) UnmarshalText ¶
UnmarshalText implements the encoding.TextUnmarshaler interface, allowing Date to be used as a map key in JSON.
type Declare ¶
type Declare struct {
Ticker string `json:"ticker"`
ID ID `json:"id"`
Currency string `json:"currency"`
Fetcher string `json:"fetcher,omitempty"` // Fetcher provides the URI to configure the financial data fetcher.
// contains filtered or unexported fields
}
Declare represents a transaction to declare a security for use in the ledger. This maps a ledger-internal ticker to a globally unique security ID and its currency. Declare represents a transaction to declare a security for use in the ledger. This maps a ledger-internal ticker to a globally unique security ID and its currency.
func NewDeclare ¶
NewDeclare creates a new Declare transaction.
func (Declare) Equal ¶
func (t Declare) Equal(other Transaction) bool
func (Declare) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Declare.
func (Declare) Rationale ¶
func (t Declare) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (Declare) Validate ¶
func (t Declare) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Declare transaction's fields. It ensures the ticker is not already declared and that the ID and currency are valid.
func (Declare) What ¶
func (t Declare) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
type Deposit ¶
type Deposit struct {
Amount Money // Amount is the quantity of cash deposited.
Settles string // Settles is an optional counterparty account that this deposit settles.
// contains filtered or unexported fields
}
Deposit represents a cash deposit. Deposit represents a transaction where cash is added to a currency account within the portfolio.
func NewDeposit ¶
NewDeposit creates a new Deposit transaction.
func (Deposit) Equal ¶
func (t Deposit) Equal(other Transaction) bool
func (Deposit) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Deposit.
func (Deposit) Rationale ¶
func (t Deposit) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (*Deposit) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Deposit.
func (Deposit) Validate ¶
func (t Deposit) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Deposit transaction's fields. It ensures the deposit amount is positive and the currency code is valid.
func (Deposit) What ¶
func (t Deposit) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
type Dividend ¶
type Dividend struct {
Amount Money // Amount is the dividend paid per share.
// contains filtered or unexported fields
}
Dividend represents a dividend payment. Dividend represents a transaction where a dividend payment is received for a held security.
func NewDividend ¶
NewDividend creates a new Dividend transaction.
func (Dividend) Equal ¶
func (t Dividend) Equal(other Transaction) bool
func (Dividend) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Dividend.
func (*Dividend) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Dividend.
type DividendInfo ¶
type DividendInfo struct {
Amount float64 // Amount per share
}
DividendInfo holds the details of a dividend payment.
type ID ¶
type ID string
ID represents the unique identifier of a security. It must follow a specific format.
It can have multiple different types.
MSSI (Market-Specific Security Identifier) ¶
MSSI represents a Market-Specific Security Identifier, a proposed standard for creating an unambiguous, composite identifier for a security listed on a specific trading venue.
The format is defined as the concatenation of an ISIN and a MIC, separated by a FULL STOP character ('.').
Formal Definition: ID = ISIN "." MIC
Example: "US0378331005.XNAS" for Apple Inc. on the NASDAQ.
This type provides a safe way to create, parse, and handle MSSIs, ensuring the format is always valid. It is based on two existing ISO standards:
- ISO 6166 (ISIN): For identifying the security.
- ISO 10383 (MIC): For identifying the market.
CurrencyPair ¶
CurrencyPair represents a currency pair identifier according to the common market convention used in foreign exchange (FX) markets.
The format is a six-character string created by concatenating two three-character ISO 4217 currency codes.
Formal Convention: CurrencyPair = <BaseCurrency><QuoteCurrency>
- BaseCurrency: The first 3-letter code, representing the currency being priced. - QuoteCurrency: The second 3-letter code, representing the currency used for the price.
This "Base/Quote" terminology is the industry standard because it is unambiguous, unlike "From/To" which can change depending on the direction of a transaction.
Example: The pair "EURUSD" represents the price of one Euro (EUR) in terms of US Dollars (USD).
Private ¶
Private represents a generic, non-standard identifier.
The format rules for a private ID are designed to prevent ambiguity with other, more specific financial identifiers. The rationale is to ensure that a private ID cannot be misinterpreted as a Market-Specific Security Identifier (MSSI) or a CurrencyPair.
Example: "My Private Equity Investment"
Rules:
- Must be at least 7 characters long.
- Must only contain alphanumeric characters and space ([ a-zA-Z0-9]).
- Must NOT contain a '.' to avoid confusion with the "ISIN.MIC" format of an MSSI.
- Must NOT be a 6-character, all-uppercase string (implicitly covered by the length rule).
func NewCurrencyPair ¶
NewCurrencyPair creates a new CurrencyPair from a base and quote currency code after validation.
func NewPrivate ¶
NewPrivate creates a new private ID after validating its format.
func ParseID ¶
ParseID attempts to parse a string into a valid ID by trying MSSI, CurrencyPair, and Private formats in order. If all parsing attempts fail, it returns a compound error detailing the reasons for each failure.
func (ID) Base ¶
Base returns the base currency of the currency pair or an empty string if the ID is not a valid currency pair.
func (ID) CurrencyPair ¶
CurrencyPair validates a 6-character string and extracts the base and quote components. It returns an error if the format is invalid.
func (ID) IsCurrencyPair ¶
IsCurrencyPair returns true if the ID represents a currency pair.
func (ID) MIC ¶
MIC returns the Market Identifier Code part of the identifier or an empty string if the ID is not an MSSI.
func (ID) MSSI ¶
MSSI validates the overall "ISIN.MIC" format and then validates each component using the ValidateISIN and ValidateMIC functions.
type Init ¶
type Init struct {
Currency string `json:"currency"`
// contains filtered or unexported fields
}
Init represents the initialization of the ledger. It sets the base currency for the ledger. It has a date and must be the first transaction.
func (Init) Equal ¶
func (t Init) Equal(other Transaction) bool
func (Init) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Init.
func (Init) Rationale ¶
func (t Init) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (Init) What ¶
func (t Init) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
type Journal ¶
type Journal struct {
// contains filtered or unexported fields
}
Journal holds a chronologically sorted list of all atomic events.
type Ledger ¶
type Ledger struct {
// contains filtered or unexported fields
}
Ledger represents a list of transactions.
In a Ledger transactions are always in chronological order.
func DecodeLedger ¶
DecodeLedger decodes transactions from a stream of JSONL data from an io.Reader, decodes each line into the appropriate transaction struct, and returns a sorted Ledger.
func DecodeValidateLedger ¶
DecodeValidateLedger decode the ledger and validate every transactions. if market is nil, skip all validations.
func FindLedger ¶
FindLedger return the unique ledger corresponding with the name. If there is only one ledger found, returns it. If the query is meant to match all ledgers and the list is empty returns an empty default ledger. In any other cases it returns an error.
func FindLedgers ¶
FindLedgers discovers and loads ledger files from a given portfolio path. The query string can be used to filter which ledgers are loaded. If query is empty, all ledgers (.jsonl files) in the path are loaded. If query specifies a ledger name (e.g., "john/bnp"), only that ledger is loaded. A ledger name is its relative path from the portfolio path, without the .jsonl extension.
func (*Ledger) AllCounterpartyAccounts ¶
AllCounterpartyAccounts returns a sequence of all unique counterparty account names.
func (*Ledger) AllSecurities ¶
AllSecurities iterates over securities declared in this ledger.
func (*Ledger) Append ¶
func (l *Ledger) Append(txs ...Transaction) error
Append appends transactions to this ledger and maintains the chronological order of transactions.
func (*Ledger) ByCurrency ¶
func (l *Ledger) ByCurrency(currency string) func(Transaction) bool
ByCurrency returns a predicate that filters transactions by currency.
func (*Ledger) CashBalance ¶
CashBalance computes the total cash in a specific currency on a specific date.
func (*Ledger) CounterPartyCurrency ¶
func (*Ledger) CounterpartyAccountBalance ¶
CounterpartyAccountBalance computes the balance of a counterparty account on a specific date.
func (*Ledger) Currencies ¶
Currencies returns a sequence of all currencies used in the ledger as of today.
func (*Ledger) Fmt ¶
Fmt creates a new, formatted ledger from the current one. It validates and sorts all transactions, applying quick fixes where applicable. This produces a canonical version of the ledger.
func (*Ledger) GenerateLog ¶
GenerateLog generates a log of reviews for each sub-period within a given date range.
func (*Ledger) GlobalInceptionDate ¶
GlobalInceptionDate returns the date of the earliest transaction, which should be the Init transaction.
func (*Ledger) HeldSecuritiesInRange ¶
HeldSecuritiesInRange returns an iterator for all securities that had a non-zero position at any point within the given date range.
func (*Ledger) InceptionDate ¶
InceptionDate scans the ledger and returns the date of the very first transaction of any kind for the given security ticker.
func (*Ledger) LastKnownMarketDataDate ¶
LastKnownMarketDataDate scans the ledger in reverse and returns the date of the most recent `update-price` or `split` transaction for the given security ticker. Deprecated: use Ledger.LastMarketDataDate instead.
func (*Ledger) LastOperationDate ¶
LastOperationDate returns the date of the last operation for a given security ticker.
func (*Ledger) Name ¶
Name returns the name of the ledger, which is its relative path from the portfolio root.
func (*Ledger) NewSnapshot ¶
NewSnapshot creates a new portfolio snapshot for a given date.
func (*Ledger) NewestTransactionDate ¶
NewestTransactionDate returns the date of the latest transaction in the ledger. It returns false if the ledger has no transactions.
func (*Ledger) OldestTransactionDate ¶
OldestTransactionDate returns the date of the earliest transaction in the ledger. It returns false if the ledger has no transactions.
func (*Ledger) Security ¶
Security return the security declared with this ticker, or nil if unknown.
func (Ledger) Transactions ¶
func (l Ledger) Transactions(accepts ...func(Transaction) bool) iter.Seq2[int, Transaction]
Transactions returns an iterator that yields each transaction in their original order with their original index.
func (*Ledger) UpdateIntraday ¶
UpdateIntraday fetches the latest intraday prices for all securities in the ledger from the tradegate provider and updates the ledger with them.
func (*Ledger) UpdateMarketData ¶
func (l *Ledger) UpdateMarketData(txs ...Transaction) (MarketDataUpdate, error)
UpdateMarketData adds transactions to the ledger.
func (*Ledger) Validate ¶
func (l *Ledger) Validate(tx Transaction) (Transaction, error)
Validate checks a transaction for correctness and applies quick fixes where applicable (e.g., resolving "sell all"). It returns the validated (and potentially modified) transaction or an error detailing any validation failures.
type MarketDataUpdate ¶
type MarketDataUpdate struct {
// contains filtered or unexported fields
}
MarketDataUpdate provides a summary of changes made during a market data update.
func (MarketDataUpdate) AddedDividends ¶
func (m MarketDataUpdate) AddedDividends() int
func (MarketDataUpdate) AddedPrices ¶
func (m MarketDataUpdate) AddedPrices() int
func (MarketDataUpdate) NewSplits ¶
func (m MarketDataUpdate) NewSplits() int
func (MarketDataUpdate) Total ¶
func (m MarketDataUpdate) Total() int
func (MarketDataUpdate) UpdatedDividends ¶
func (m MarketDataUpdate) UpdatedDividends() int
func (MarketDataUpdate) UpdatedPrices ¶
func (m MarketDataUpdate) UpdatedPrices() int
func (MarketDataUpdate) UpdatedSplits ¶
func (m MarketDataUpdate) UpdatedSplits() int
type Money ¶
type Money struct {
// contains filtered or unexported fields
}
Money represents a monetary value.
func (Money) GreaterThan ¶
func (Money) GreaterThanOrEqual ¶
func (Money) IsNegative ¶
func (Money) IsPositive ¶
func (Money) LessThanOrEqual ¶
func (Money) MarshalJSON ¶
func (Money) SignedString ¶
SignedString returns the string representation of the money value with a sign. 0 is represented as a ""
type Performance ¶
type Performance struct {
Start, End Money
Return Percent // TWR return if available or price change
}
Performance holds the starting value and the calculated change for a specific range.
func NewPerformance ¶
func NewPerformance(start, end Money) Performance
func NewPerformanceWithReturn ¶
func NewPerformanceWithReturn(start, end Money, ret Percent) Performance
func (Performance) Change ¶
func (p Performance) Change() Money
func (Performance) Percent ¶
func (p Performance) Percent() Percent
type Period ¶
type Period int
func ParsePeriod ¶
func (Period) ToDateName ¶
ToDateName returns the "-to-Date" name for the period (e.g., "Month-to-Date").
type ProviderResponse ¶
type ProviderResponse struct {
Prices map[Date]float64
Splits map[Date]SplitInfo
Dividends map[Date]DividendInfo
}
ProviderResponse holds the data returned by a data provider for a single security. It's a generic container for prices, splits, and dividends.
type Quantity ¶
type Quantity struct {
// contains filtered or unexported fields
}
func (Quantity) GreaterThan ¶
func (Quantity) IsNegative ¶
func (Quantity) IsPositive ¶
func (Quantity) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for baseCmd.
func (*Quantity) UnmarshalJSON ¶
type Range ¶
type Range struct{ From, To Date }
Range represents a range of dates.
func (Range) Identifier ¶
Identifier compute a unique identifier for the Range. If the period is defined, use a short insighful name
type Review ¶
type Review struct {
// contains filtered or unexported fields
}
Review represents an analysis of the portfolio over a specific period (Range). It calculates period-based metrics by comparing two Snapshots: one at the start of the period and one at the end.
func (*Review) AssetCostBasis ¶
func (r *Review) AssetCostBasis(ticker string, method CostBasisMethod) Money
AssetCostBasis calculates the cost basis of a single security at the end of the review period. This is used for the "Invested" column in reports.
func (*Review) AssetDividends ¶
AssetDividends calculates the dividends received for a single security during the period.
func (*Review) AssetMarketGain ¶
AssetMarketGain calculates the change in a security's value due to price movements during the period.
func (*Review) AssetNetTradingFlow ¶
AssetNetTradingFlow calculates the net cash invested into or divested from a single security during the period.
func (*Review) AssetRealizedGains ¶
func (r *Review) AssetRealizedGains(ticker string, method CostBasisMethod) Money
AssetRealizedGains calculates the realized gains for a single security during the period.
func (*Review) AssetTimeWeightedReturn ¶
AssetTimeWeightedReturn calculates the time-weighted return for a single security over the review period.
func (*Review) AssetTotalReturn ¶
AssetTotalReturn calculates the total return for a single security during the period, combining market gains/losses and dividend income.
func (*Review) CashChange ¶
CashChange calculates the net change in the total cash balance during the review period.
func (*Review) CashFlow ¶
CashFlow calculates the total net cash that has moved into or out of the portfolio from external sources during the review period.
func (*Review) CounterpartyChange ¶
CounterpartyChange calculates the net change in the total counterparty balance during the review period.
func (*Review) CurrencyTimeWeightedReturn ¶
CurrencyTimeWeightedReturn calculates the time-weighted return for a currency's exchange rate over the review period.
func (*Review) DividendReturn ¶
DividendReturn calculates the return from dividends as a percentage of the starting portfolio value.
func (*Review) Dividends ¶
Dividends calculates the total income received from dividends during the review period.
func (*Review) End ¶
End returns the snapshot at the end of the review period (taken on `period.To`).
func (*Review) MarketGain ¶
MarketGain calculates the change in security value due to price movements, isolated from the impact of buying or selling.
func (*Review) NetTradingFlow ¶
NetTradingFlow calculates the total net cash invested into or divested from all securities during the review period.
func (*Review) PortfolioChange ¶
PortfolioChange calculates the net change in total portfolio value during the review period.
func (*Review) RealizedGains ¶
func (r *Review) RealizedGains(method CostBasisMethod) Money
RealizedGains calculates the sum of all profits and losses 'locked in' through the sale of securities during the review period.
func (*Review) Start ¶
Start returns the snapshot at the beginning of the review period (taken on `period.From - 1`).
func (*Review) TimeWeightedReturn ¶
TimeWeightedReturn calculates the compound rate of growth for a security over the review period, eliminating the distorting effects of cash flows.
func (*Review) TotalCostBasis ¶
func (r *Review) TotalCostBasis(method CostBasisMethod) Money
TotalCostBasis calculates the total cost basis of all securities held at the end of the review period. This is used for the "Invested" total in reports.
func (*Review) TotalMarketChange ¶
TotalMarketChange calculates the net change in the total market value of all securities during the review period.
func (*Review) TotalReturn ¶
TotalReturn calculates the total economic benefit from the portfolio over a period, combining market gains/losses and dividend income.
func (*Review) Transactions ¶
func (r *Review) Transactions() []Transaction
Transactions returns a slice of all transactions that occurred within the review period.
func (*Review) UnrealizedGains ¶
func (r *Review) UnrealizedGains(method CostBasisMethod) Money
UnrealizedGains calculates the change in unrealized gains for a single security during the period.
type Security ¶
type Security struct {
// contains filtered or unexported fields
}
Security represents a publicly or privately tradeable asset, such as a stock, ETF, or currency pair.
func NewSecurity ¶
func (Security) Description ¶
Description returns the user-provided description for the security.
type Sell ¶
type Sell struct {
Quantity Quantity // Quantity is the number of shares or units sold.
Amount Money // Amount is the total proceeds from the sale.
// contains filtered or unexported fields
}
Sell represents a sell transaction. Sell represents a transaction where a quantity of a security is sold for a specified amount.
func NewSell ¶
NewSell creates a new Sell transaction. If the quantity is set to 0, it signifies a "sell all" instruction. The actual number of shares will be determined during the validation phase based on the portfolio's position on the transaction date.
func (Sell) Equal ¶
func (t Sell) Equal(other Transaction) bool
func (Sell) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Sell.
func (*Sell) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Sell. It handles the custom structure where amount and currency are separate fields.
func (Sell) Validate ¶
func (t Sell) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Sell transaction's fields. It handles the "sell all" case by resolving a quantity of 0 to the total position size on the transaction date. It ensures the final quantity and price are positive and that the position is sufficient to cover the sale. It now accepts a Ledger object.
type Snapshot ¶
type Snapshot struct {
// contains filtered or unexported fields
}
Snapshot represents a view of the portfolio at a single point in time. It is a stateless calculator that computes all values on-the-fly by processing journal events up to its 'on' date.
func (*Snapshot) CashFlow ¶
CashFlow calculates the total net cash that has moved into or out of the portfolio from external sources for a specific currency since inception.
func (*Snapshot) CostBasis ¶
func (s *Snapshot) CostBasis(ticker string, method CostBasisMethod) Money
CostBasis calculates the total cost basis of a security held on the snapshot's date.
func (*Snapshot) Counterparties ¶
Counterparties returns an iterator over all declared counterparty accounts up to the snapshot's date. The order is based on the date of their declaration.
func (*Snapshot) Counterparty ¶
Counterparty returns the balance of a specific counterparty account on the snapshot's date.
func (*Snapshot) Currencies ¶
Currencies returns an iterator over all currencies encountered up to the snapshot's date. This includes the reporting currency, currencies from cash transactions, and security/counterparty declarations. The order is based on their first appearance.
func (*Snapshot) Dividends ¶
Dividends calculates the total income received from dividends for a specific security since inception.
func (*Snapshot) ExchangeRate ¶
ExchangeRate finds the last known exchange rate for a given currency on or before the snapshot's date. The rate is the value of 1 unit of the foreign currency in the portfolio's reporting currency.
func (*Snapshot) LastMarketDataDate ¶
LastMarketDataDate returns the date of the most recent market data (price or split) for a security on or before the snapshot's date.
func (*Snapshot) MarketValue ¶
MarketValue calculates the market value of a single security on the snapshot's date.
func (*Snapshot) NetTradingFlow ¶
NetTradingFlow calculates the total net cash invested into or divested from a specific security since inception. A positive value indicates a net cash outflow (more spent on buys than received from sells).
func (*Snapshot) Position ¶
Position calculates the quantity held of a single security on the snapshot's date.
func (*Snapshot) Price ¶
Price finds the last known price for a security on or before the snapshot's date.
func (*Snapshot) RealizedGains ¶
func (s *Snapshot) RealizedGains(ticker string, method CostBasisMethod) Money
RealizedGains calculates the sum of all profits and losses 'locked in' through the sale of a specific security since inception.
func (*Snapshot) ReportingCurrency ¶
ReportingCurrency returns the reporting currency of the journal associated with the snapshot.
func (*Snapshot) Securities ¶
Securities returns an iterator over all declared security tickers up to the snapshot's date. The order is based on the date of their declaration.
func (*Snapshot) SecurityDetails ¶
SecurityDetails finds the declaration for a given ticker.
func (*Snapshot) TotalCash ¶
TotalCash returns the total cash balance across all currencies, converted to the reporting currency.
func (*Snapshot) TotalCashFlow ¶
TotalCashFlow returns the total cash flow across all currencies, converted to the reporting currency.
func (*Snapshot) TotalCostBasis ¶
func (s *Snapshot) TotalCostBasis(method CostBasisMethod) Money
TotalCostBasis calculates the total cost basis of all securities.
func (*Snapshot) TotalCounterparty ¶
TotalCounterparty returns the total balance across all counterparty accounts, converted to the reporting currency.
func (*Snapshot) TotalCounterpartyIn ¶
TotalCounterpartyIn returns the total balance across all counterparty accounts in a specific currency. The returned value is in that currency, not the reporting currency.
func (*Snapshot) TotalDividends ¶
TotalDividends returns the total dividends received across all securities, converted to the reporting currency.
func (*Snapshot) TotalMarket ¶
TotalMarket returns the total market value of all securities in the portfolio.
func (*Snapshot) TotalMarketIn ¶
TotalMarketIn returns the total market value of all securities denominated in a specific currency. The returned value is in that currency, not the reporting currency.
func (*Snapshot) TotalNetTradingFlow ¶
TotalNetTradingFlow returns the total net trading flow across all securities, converted to the reporting currency.
func (*Snapshot) TotalPortfolio ¶
TotalPortfolio returns the total value of the portfolio, including securities, cash, and counterparty accounts.
func (*Snapshot) TotalRealizedGains ¶
func (s *Snapshot) TotalRealizedGains(method CostBasisMethod) Money
TotalRealizedGains returns the total realized gains across all securities, converted to the reporting currency.
func (*Snapshot) TotalUnrealizedGains ¶
func (s *Snapshot) TotalUnrealizedGains(method CostBasisMethod) Money
TotalUnrealizedGains calculates the total unrealized gains across all securities.
func (*Snapshot) UnrealizedGains ¶
func (s *Snapshot) UnrealizedGains(ticker string, method CostBasisMethod) Money
UnrealizedGains calculates the paper profit or loss on a security. It's the difference between the current market value and the cost basis.
func (*Snapshot) VirtualAssetValue ¶
VirtualAssetValue simulates the growth of a 1-unit investment in a security. This is the core of the Time-Weighted Return calculation. It tracks a "virtual" portfolio that buys into a security when the actual position goes from zero to non-zero, and sells out when the actual position returns to zero.
func (*Snapshot) VirtualTotalValue ¶
VirtualTotalValue simulates the growth of a 1-unit investment in the entire portfolio. This is the core of the portfolio-wide Time-Weighted Return calculation. It tracks the portfolio's value and adjusts for external cash flows.
type Split ¶
type Split struct {
Numerator int64 `json:"num"`
Denominator int64 `json:"den"`
// contains filtered or unexported fields
}
Split represents a stock split event for a security.
func (Split) Equal ¶
func (t Split) Equal(other Transaction) bool
func (Split) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Split.
func (*Split) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Split.
type Transaction ¶
type Transaction interface {
What() CommandType // What returns the command type of the transaction (e.g., "buy", "sell").
When() Date // When returns the date on which the transaction occurred.
Equal(Transaction) bool
Validate(ledger *Ledger) (Transaction, error)
}
Transaction defines the common interface for all types of financial transactions that can be recorded in the ledger.
type UpdatePrice ¶
type UpdatePrice struct {
Prices map[string]decimal.Decimal
// contains filtered or unexported fields
}
UpdatePrice represents a transaction to record the prices of multiple securities on a specific date.
func NewUpdatePrice ¶
func NewUpdatePrice(date Date, ticker string, price Money) UpdatePrice
NewUpdatePrice creates a new UpdatePrice transaction for a single security. This is kept for backward compatibility and ease of transition.
func NewUpdatePrices ¶
func NewUpdatePrices(date Date, prices map[string]decimal.Decimal) UpdatePrice
NewUpdatePrices creates a new UpdatePrice transaction for multiple securities.
func (UpdatePrice) Equal ¶
func (t UpdatePrice) Equal(other Transaction) bool
func (UpdatePrice) MarshalJSON ¶
func (t UpdatePrice) MarshalJSON() ([]byte, error)
MarshalJSON implements the json.Marshaler interface for UpdatePrice.
func (UpdatePrice) PricesIter ¶
PricesIter returns an iterator that yields ticker and price pairs in a stable, sorted order.
func (UpdatePrice) Rationale ¶
func (t UpdatePrice) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (*UpdatePrice) UnmarshalJSON ¶
func (t *UpdatePrice) UnmarshalJSON(data []byte) error
UnmarshalJSON implements the json.Unmarshaler interface for UpdatePrice.
func (UpdatePrice) Validate ¶
func (t UpdatePrice) Validate(ledger *Ledger) (Transaction, error)
Validate checks the UpdatePrice transaction's fields.
func (UpdatePrice) What ¶
func (t UpdatePrice) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
type Withdraw ¶
type Withdraw struct {
Amount Money // Amount is the quantity of cash withdrawn.
Settles string // Settles is an optional counterparty account that this withdrawal settles.
// contains filtered or unexported fields
}
Withdraw represents a cash withdrawal. Withdraw represents a transaction where cash is removed from a currency account within the portfolio.
func NewWithdraw ¶
NewWithdraw creates a new Withdraw transaction. If the amount is set to 0, it signifies a "withdraw all" instruction for the specified currency. The actual amount will be determined during the validation phase based on the cash balance on the transaction date.
func (Withdraw) Equal ¶
func (t Withdraw) Equal(other Transaction) bool
func (Withdraw) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface for Withdraw.
func (Withdraw) Rationale ¶
func (t Withdraw) Rationale() string
Rationale returns the memo associated with the transaction, which can provide additional context or rationale.
func (*Withdraw) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface for Withdraw.
func (Withdraw) Validate ¶
func (t Withdraw) Validate(ledger *Ledger) (Transaction, error)
Validate checks the Withdraw transaction's fields. It handles a "withdraw all" case if the amount is 0, ensures the final amount is positive, and verifies there is sufficient cash to cover the withdrawal
func (Withdraw) What ¶
func (t Withdraw) What() CommandType
What returns the command name for the transaction, which is used to identify the type of transaction.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cmd implements the CLI application to manage a portfolio.
|
Package cmd implements the CLI application to manage a portfolio. |
|
Package main provides the entry point for the `pcs` command-line tool.
|
Package main provides the entry point for the `pcs` command-line tool. |
