portfolio

package module
v0.0.0-...-9cd1a11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2026 License: BSD-2-Clause Imports: 24 Imported by: 0

README

pcs-large

Go Reference

pcs: Your Private, Unified Portfolio Tracker

For the long-term investor with a scattered and diverse portfolio, pcs is the durable command-line tool that unifies your entire financial world.

By placing your financial data directly under your control in simple, local text files, pcs provides a permanent and auditable record of your wealth. It is built to last.

pcs provides essential clarity and insights, making it effortless to answer the two most critical questions at any point in your financial journey: "What is my net worth?" and "How is it performing?".

The Solution: A Unified, Durable Portfolio

pcs solves this by being asset-agnostic and built for durability.

  • Unify Everything: It is designed from the ground up to track a diverse range of assets, from public stocks and private funds to liabilities, consolidating every part of your wealth into one coherent picture.
  • Built to Last: Your data lives in a simple, local text file (transactions.jsonl). You own it forever. The text-based format works perfectly with version control (like Git), creating an immutable, auditable history of your wealth that will outlast any proprietary cloud service.
  • Essential Clarity: Financial tools often overwhelm users with complex analytics. pcs focuses on providing simple, clear answers to the core questions that matter most.

Getting Started: Your First Unified Portfolio

This tutorial will walk you through the process of setting up your portfolio and tracking your first investment.

Installation

To get started, ensure you have Go installed on your system. Then, you can install pcs with a single command:

go install github.com/etnz/portfolio/cmd/pcs@latest
Declaring Your Assets

Before you can track an asset, you need to know exactly what asset you want to track. For instance Apple's stocks can be exchanged in USD on the Nasdaq, but also as EUR on the XETRA exchange.

Let's declare a public stock (Apple) and a private fund in your corporate savings plan. declaring is given them a short mnemonic name to include all that identifying information about the stock.

pcs declare -d 2025-08-27 -s AAPL -id US0378331005.XETR -c EUR
✅ Successfully recorded transaction in ledger "ledger".

[!NOTE] id uses Apple stock's ISIN followed by the exchange MIC code (XETR for XETRA). You can find this information on any financial websites. pcs search Apple can also help you find it.

Your corporate savings plan let you buy shares of funds that unfortunately are often not publicly traded. You can still track it by giving it a unique "private" identifier, and updating them manually (or by other tricks):

pcs declare -d 2025-08-27 -s BankFund1 -id My-bank-Fund1 -c EUR
✅ Successfully recorded transaction in ledger "ledger".

[!NOTE] The -id is a private identifier that uniquely represents your bank's private fund.

Publicly traded securities can be fetched automatically. pcs supports eodhd out of the box, but its extension mechanism allows you to use your preferred data provider.

Privately traded securities, often found in bank savings accounts, can be more tedious to update. You can either update the fund price manually using the CLI or automate the process by writing or finding a suitable provider extension.

Recording Transactions

Let's record that we have deposited some cash into your account.

pcs deposit -d 2025-08-27 -a 10000 -c EUR
✅ Successfully recorded transaction in ledger "ledger".

[!NOTE]

In the whole documentation we always explicitly set the date for clarity, but the defaut date is usually correct.

Let's record that we bought some Apple's stock with that money.

pcs buy -d 2025-08-27 -s AAPL -q 10 -a 1500.0
✅ Successfully recorded transaction in ledger "ledger".

Let's record a buy in the corporate savings plan.

pcs buy -d 2025-08-27 -s BankFund1 -q 100 -a 1200.0
✅ Successfully recorded transaction in ledger "ledger".
Keeping Your Portfolio Up-to-Date

Recording the transaction you have initiated is not enough to compute the value of your portfolio. You also need the latest information from the market. pcs can automatically fetch market data from providers. Each provider has its own command, for example:

pcs eodhd fetch
pcs insee fetch

You can also manually set the price for any asset using the pcs price command.

However for the purpose of the tutorial, we are only going to use the manual method. We can read from any financial site Apple's closing price on 2025-08-27:

pcs price -s AAPL -d 2025-08-27 -p 193.20
✅ Successfully recorded transaction in ledger "ledger".

From your corporate saving bank site you can get the price on 2025-08-27:

pcs price -s BankFund1 -d 2025-08-27 -p 11.23
✅ Successfully recorded transaction in ledger "ledger".
The Payoff: Reporting

Now, you can see a unified view of your portfolio:

pcs review -d 2025-08-27
# ledger Review for 2025-08-27

  *As of 2006-01-02 15:04:05*

   **Total Portfolio Value** | **€10,355.00** 
  ---------------------------|----------------
              Previous Value |          €0.00 
                             |                
                Capital Flow |    +€10,000.00 
              + Market Gains |       +€355.00 
               + Forex Gains |              - 
            **= Net Change** | **€10,355.00** 
                             |                
                 Cash Change |     +€7,300.00 
     + Counterparties Change |              - 
       + Market Value Change |     +€3,055.00 
            **= Net Change** | **€10,355.00** 
                             |                
                   Dividends |              - 
              + Market Gains |       +€355.00 
               + Forex Gains |              - 
            **=Total Gains** |   **+€355.00** 

  ## Accounts

   **Cash Accounts** |         Value | Forex % 
  -------------------|---------------|---------
                 EUR |     €7,300.00 |       - 
           **Total** | **€7,300.00** |         

   **Counterparty Accounts** | Value 
  ---------------------------|-------
                   **Total** | **-** 

  ## Consolidated Asset Report

   Asset     | Start Value |     End Value |   Trading Flow |  Market Gain | Realized Gain | Unrealized Gain | Dividends |       TWR 
  -----------|-------------|---------------|----------------|--------------|---------------|-----------------|-----------|-----------
   AAPL      |        0.00 |     €1,932.00 |     +€1,500.00 |     +€432.00 |             - |        +€432.00 |         - |         - 
   BankFund1 |        0.00 |     €1,123.00 |     +€1,200.00 |      -€77.00 |             - |         -€77.00 |         - |    -0.00% 
   **Total** |   **€0.00** | **€3,055.00** | **+€2,700.00** | **+€355.00** |         **-** |    **+€355.00** |     **-** | **+NaN%** 

  ## Transactions

  • 2025-08-27: Declare "AAPL" as "US0378331005.XETR" in EUR
  •           : Declare "BankFund1" as "My-bank-Fund1" in EUR
  •           : Update price for "AAPL"=193.2000
  •           : Update price for "BankFund1"=11.2300
  •           : Deposit €10,000.00
  •           : Buy 10 of "AAPL" for €1,500.00
  •           : Buy 100 of "BankFund1" for €1,200.00

User Manual

For a detailed guide on all the features and commands, please refer to the User Manual.

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

View Source
const DateFormat = "2006-01-02" // write date format

DateFormat is the format used to represent dates as strings in ISO-8601 format.

View Source
const DatetimeFormat = time.RFC3339
View Source
const Day = 24 * time.Hour

Variables

This section is empty.

Functions

func AcceptAll

func AcceptAll(Transaction) bool

AcceptAll transactions.

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

func EncodeLedger(w io.Writer, ledger *Ledger) error

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

func SaveLedger(path string, ledger *Ledger) error

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

func ValidateCurrency(code string) error

ValidateCurrency checks if a string is a validly formatted ISO 4217 currency code.

func ValidateISIN

func ValidateISIN(isin string) error

ValidateISIN checks if a string is a validly formatted ISIN. It returns nil if valid, or a descriptive error if invalid.

func ValidateMIC

func ValidateMIC(mic string) error

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

func NewAccrue(day Date, memo, counterparty string, amount Money) Accrue

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

func NewCreatedAccrue(day Date, memo, counterparty string, amount Money) Accrue

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) Currency

func (t *Accrue) Currency() string

Currency returns the currency of the transaction.

func (Accrue) Equal

func (t Accrue) Equal(other Transaction) bool

func (Accrue) MarshalJSON

func (t Accrue) MarshalJSON() ([]byte, error)

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

func (t *Accrue) UnmarshalJSON(data []byte) error

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.

func (Accrue) When

func (t Accrue) When() Date

When returns the date of the 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 NewBuy

func NewBuy(day Date, memo, security string, quantity Quantity, amount Money) Buy

NewBuy creates a new Buy transaction.

func (*Buy) Currency

func (t *Buy) Currency() string

func (Buy) Equal

func (t Buy) Equal(other Transaction) bool

func (Buy) MarshalJSON

func (t Buy) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for Buy.

func (*Buy) UnmarshalJSON

func (t *Buy) UnmarshalJSON(data []byte) error

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

type Convert struct {
	FromAmount Money
	ToAmount   Money
	// contains filtered or unexported fields
}

Convert represents an internal currency conversion. Convert represents an internal currency conversion.

func NewConvert

func NewConvert(day Date, memo string, fromAmount Money, toAmount Money) Convert

NewConvert creates a new Convert transaction.

func (Convert) Equal

func (t Convert) Equal(other Transaction) bool

func (*Convert) FromCurrency

func (t *Convert) FromCurrency() string

func (Convert) MarshalJSON

func (t Convert) MarshalJSON() ([]byte, error)

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 (t *Convert) ToCurrency() string

func (*Convert) UnmarshalJSON

func (t *Convert) UnmarshalJSON(data []byte) error

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.

func (Convert) When

func (t Convert) When() Date

When returns the date of the 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 MustParse

func MustParse(str string) Date

MustParse is like Parse but panics on error.

func NewDate

func NewDate(year int, month time.Month, day int) Date

NewDate returns a normalized Date for the given year, month, and day.

func ParseDate

func ParseDate(str string) (Date, error)

ParseDate parses a Date from a string. It is lenient and accepts formats like "2025-7-1".

func Today

func Today() Date

Today returns the current date.

func (Date) Add

func (d Date) Add(i int) Date

Add returns a new Date with the given number of days added.

func (Date) AddMonth

func (d Date) AddMonth(i int) Date

AddMonth returns a new Date with the given number of days added.

func (Date) After

func (d Date) After(x Date) bool

After reports whether the day d is after x.

func (Date) Before

func (d Date) Before(x Date) bool

Before reports whether the day d is before x.

func (Date) Compare

func (d Date) Compare(e Date) int

Compare effectively compare dates for faster sorting.

func (Date) Day

func (d Date) Day() int

Day returns current day of the month.

func (Date) DayString

func (d Date) DayString() string

DayString return d.String() + if the day is today the hh:mm:ss time.

func (Date) EndOf

func (d Date) EndOf(period Period) Date

EndOf returns the date of end of a given period

func (Date) Format

func (d Date) Format(format string) string

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) Full

func (d Date) Full() string

Full format the date in date-time RFC3339

func (Date) ISOWeek

func (d Date) ISOWeek() (year, week int)

ISOWeek returns the ISO 8601 year and week number in which d occurs.

func (Date) IsToday

func (d Date) IsToday() bool

func (Date) IsZero

func (d Date) IsZero() bool

IsZero returns true if the date is the zero value.

func (Date) MarshalJSON

func (d Date) MarshalJSON() ([]byte, error)

func (Date) Month

func (d Date) Month() time.Month

Month returns the month of the date.

func (Date) Quarter

func (d Date) Quarter() int

Quarter return the quarter 'd' is in in the range [1-4]

func (Date) Range

func (d Date) Range(period Period) Range

Range returns the date range for a given period relative to the date.

func (Date) StartOf

func (d Date) StartOf(period Period) Date

StartOf returns the date of begining of a given period

func (Date) String

func (d Date) String() string

String format the date in date RFC3339

func (Date) Unix

func (d Date) Unix() int64

Unix returns t as a Unix time, the number of seconds elapsed since January 1, 1970 UTC.

func (*Date) UnmarshalJSON

func (d *Date) UnmarshalJSON(bytes []byte) error

UnmarshalJSON implements the json specific way to unmarshall a date from a json string.

func (*Date) UnmarshalText

func (dd *Date) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface, allowing Date to be used as a map key in JSON.

func (Date) Weekday

func (d Date) Weekday() time.Weekday

Weekday returns the day of the week for the date.

func (Date) Year

func (d Date) Year() int

Year returns current year.

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

func NewDeclare(day Date, memo, ticker string, id ID, currency string, fetcher string) Declare

NewDeclare creates a new Declare transaction.

func (Declare) Equal

func (t Declare) Equal(other Transaction) bool

func (Declare) MarshalJSON

func (t Declare) MarshalJSON() ([]byte, error)

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.

func (Declare) When

func (t Declare) When() Date

When returns the date of the 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

func NewDeposit(day Date, memo string, amount Money, settles string) Deposit

NewDeposit creates a new Deposit transaction.

func (Deposit) Currency

func (t Deposit) Currency() string

func (Deposit) Equal

func (t Deposit) Equal(other Transaction) bool

func (Deposit) MarshalJSON

func (t Deposit) MarshalJSON() ([]byte, error)

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

func (t *Deposit) UnmarshalJSON(data []byte) error

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.

func (Deposit) When

func (t Deposit) When() Date

When returns the date of the 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

func NewDividend(day Date, memo, security string, amount Money) Dividend

NewDividend creates a new Dividend transaction.

func (Dividend) Equal

func (t Dividend) Equal(other Transaction) bool

func (Dividend) MarshalJSON

func (t Dividend) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for Dividend.

func (*Dividend) UnmarshalJSON

func (t *Dividend) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for Dividend.

func (Dividend) Validate

func (t Dividend) Validate(ledger *Ledger) (Transaction, error)

Validate checks the Dividend transaction's fields. It ensures the dividend amount is positive.

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:

  1. Must be at least 7 characters long.
  2. Must only contain alphanumeric characters and space ([ a-zA-Z0-9]).
  3. Must NOT contain a '.' to avoid confusion with the "ISIN.MIC" format of an MSSI.
  4. Must NOT be a 6-character, all-uppercase string (implicitly covered by the length rule).

func NewCurrencyPair

func NewCurrencyPair(base, quote string) (ID, error)

NewCurrencyPair creates a new CurrencyPair from a base and quote currency code after validation.

func NewISIN

func NewISIN(isin string) (ID, error)

NewPrivate creates a new private ID after validating its format.

func NewMSSI

func NewMSSI(isin, mic string) (ID, error)

NewMSSI creates a new MSSI from its constituent parts after basic validation.

func NewPrivate

func NewPrivate(s string) (ID, error)

NewPrivate creates a new private ID after validating its format.

func ParseID

func ParseID(s string) (ID, error)

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

func (id ID) Base() string

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

func (id ID) CurrencyPair() (base string, quote string, err error)

CurrencyPair validates a 6-character string and extracts the base and quote components. It returns an error if the format is invalid.

func (ID) ISIN

func (id ID) ISIN() (isin string, err error)

ISIN Validate that the ID is isin only (for fund) and returns the isin part.

func (ID) IsCurrencyPair

func (id ID) IsCurrencyPair() bool

IsCurrencyPair returns true if the ID represents a currency pair.

func (ID) IsISIN

func (id ID) IsISIN() bool

IsISIN returns true if the ID represents a ISIN.

func (ID) IsMSSI

func (id ID) IsMSSI() bool

IsMSSI returns true if the ID represents a MSSI.

func (ID) MIC

func (id ID) MIC() string

MIC returns the Market Identifier Code part of the identifier or an empty string if the ID is not an MSSI.

func (ID) MSSI

func (id ID) MSSI() (isin string, mic string, err error)

MSSI validates the overall "ISIN.MIC" format and then validates each component using the ValidateISIN and ValidateMIC functions.

func (ID) Private

func (id ID) Private() (private string, err error)

Private validates that a string is a valid Private ID.

func (ID) Quote

func (id ID) Quote() string

Quote returns the quote currency of the currency pair or an empty string if the ID is not a valid currency pair.

func (ID) String

func (id ID) String() string

String implements the fmt.Stringer interface.

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 NewInit

func NewInit(date Date, memo string, currency string) Init

NewInit creates a new Init transaction.

func (Init) Equal

func (t Init) Equal(other Transaction) bool

func (Init) MarshalJSON

func (t Init) MarshalJSON() ([]byte, error)

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) Validate

func (t Init) Validate(ledger *Ledger) (Transaction, error)

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.

func (Init) When

func (t Init) When() Date

When returns the date of the 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

func DecodeLedger(r io.Reader) (*Ledger, error)

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

func DecodeValidateLedger(r io.Reader) (*Ledger, error)

DecodeValidateLedger decode the ledger and validate every transactions. if market is nil, skip all validations.

func FindLedger

func FindLedger(path, query string) (*Ledger, error)

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

func FindLedgers(path, query string) ([]*Ledger, error)

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 NewLedger

func NewLedger() *Ledger

NewLedger creates an empty ledger.

func (*Ledger) AllCounterpartyAccounts

func (l *Ledger) AllCounterpartyAccounts() iter.Seq[string]

AllCounterpartyAccounts returns a sequence of all unique counterparty account names.

func (*Ledger) AllSecurities

func (l *Ledger) AllSecurities() iter.Seq[Security]

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

func (l *Ledger) CashBalance(currency string, on Date) Money

CashBalance computes the total cash in a specific currency on a specific date.

func (*Ledger) CounterPartyCurrency

func (l *Ledger) CounterPartyCurrency(account string) (cur string, exists bool)

func (*Ledger) CounterpartyAccountBalance

func (l *Ledger) CounterpartyAccountBalance(account string, on Date) Money

CounterpartyAccountBalance computes the balance of a counterparty account on a specific date.

func (*Ledger) Currencies

func (ledger *Ledger) Currencies() iter.Seq[string]

Currencies returns a sequence of all currencies used in the ledger as of today.

func (*Ledger) Currency

func (ledger *Ledger) Currency() string

func (*Ledger) Fmt

func (l *Ledger) Fmt() (*Ledger, error)

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

func (l *Ledger) GenerateLog(r Range, period Period) ([]*Review, error)

GenerateLog generates a log of reviews for each sub-period within a given date range.

func (*Ledger) GlobalInceptionDate

func (l *Ledger) GlobalInceptionDate() Date

GlobalInceptionDate returns the date of the earliest transaction, which should be the Init transaction.

func (*Ledger) HeldSecuritiesInRange

func (l *Ledger) HeldSecuritiesInRange(period Range) iter.Seq[Security]

HeldSecuritiesInRange returns an iterator for all securities that had a non-zero position at any point within the given date range.

func (*Ledger) InceptionDate

func (l *Ledger) InceptionDate(security string) Date

InceptionDate scans the ledger and returns the date of the very first transaction of any kind for the given security ticker.

func (*Ledger) Journal

func (l *Ledger) Journal() *Journal

Journal returns the ledger's journal.

func (*Ledger) LastKnownMarketDataDate

func (l *Ledger) LastKnownMarketDataDate(security string) Date

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

func (ledger *Ledger) LastOperationDate(s string) Date

LastOperationDate returns the date of the last operation for a given security ticker.

func (*Ledger) Name

func (ledger *Ledger) Name() string

Name returns the name of the ledger, which is its relative path from the portfolio root.

func (*Ledger) NewReview

func (l *Ledger) NewReview(period Range) *Review

NewReview creates a new portfolio review for a given period.

func (*Ledger) NewSnapshot

func (l *Ledger) NewSnapshot(on Date) *Snapshot

NewSnapshot creates a new portfolio snapshot for a given date.

func (*Ledger) NewestTransactionDate

func (l *Ledger) NewestTransactionDate() Date

NewestTransactionDate returns the date of the latest transaction in the ledger. It returns false if the ledger has no transactions.

func (*Ledger) OldestTransactionDate

func (l *Ledger) OldestTransactionDate() Date

OldestTransactionDate returns the date of the earliest transaction in the ledger. It returns false if the ledger has no transactions.

func (*Ledger) Position

func (l *Ledger) Position(on Date, ticker string) Quantity

Position computes the current holding for a ticker

func (*Ledger) Security

func (l *Ledger) Security(ticker string) *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

func (l *Ledger) UpdateIntraday() error

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 M

func M[T float32 | float64 | int | int32 | int64 | uint | uint32 | uint64 | decimal.Decimal](value T, currency string) Money

func (Money) Add

func (m Money) Add(n Money) Money

binary operators.

func (Money) AsFloat deprecated

func (m Money) AsFloat() float64

Deprecated: AsFloat should no longer be used, the purpose is to keep the calculation exact.

func (Money) Currency

func (m Money) Currency() string

func (Money) Div

func (m Money) Div(n Quantity) Money

func (Money) DivPrice

func (m Money) DivPrice(n Money) Quantity

func (Money) Equal

func (m Money) Equal(n Money) bool

func (Money) GreaterThan

func (m Money) GreaterThan(n Money) bool

func (Money) GreaterThanOrEqual

func (m Money) GreaterThanOrEqual(n Money) bool

func (Money) IsNegative

func (m Money) IsNegative() bool

func (Money) IsPositive

func (m Money) IsPositive() bool

func (Money) IsZero

func (m Money) IsZero() bool

func (Money) LessThan

func (m Money) LessThan(amount Money) bool

func (Money) LessThanOrEqual

func (m Money) LessThanOrEqual(n Money) bool

func (Money) MarshalJSON

func (m Money) MarshalJSON() ([]byte, error)

func (Money) Mul

func (m Money) Mul(n Quantity) Money

func (Money) Neg

func (m Money) Neg() Money

func (Money) SignedString

func (m Money) SignedString() string

SignedString returns the string representation of the money value with a sign. 0 is represented as a ""

func (Money) String

func (m Money) String() string

String returns the string representation of the money value.

func (Money) Sub

func (m Money) Sub(n Money) Money

type Percent

type Percent float64

func (Percent) Equal

func (p Percent) Equal(q Percent) bool

func (Percent) SignedString

func (p Percent) SignedString() string

func (Percent) String

func (p Percent) String() string

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
const (
	Daily Period = iota
	Weekly
	Monthly
	Quarterly
	Yearly
)

func ParsePeriod

func ParsePeriod(p string) (Period, error)

func (Period) Name

func (p Period) Name() string

Name returns the singular noun for the period (e.g., "day", "week", "month").

func (Period) Range

func (p Period) Range(d Date) Range

Range returns a Range for the given period containing the date d.

func (Period) String

func (p Period) String() string

func (Period) ToDateName

func (p Period) ToDateName() string

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 Q

func Q[T float32 | float64 | int | int32 | int64 | uint | uint32 | uint64 | decimal.Decimal](value T) Quantity

func (Quantity) Add

func (t Quantity) Add(p Quantity) Quantity

func (Quantity) Div

func (t Quantity) Div(p Quantity) Quantity

func (Quantity) Equal

func (t Quantity) Equal(p Quantity) bool

func (Quantity) GreaterThan

func (t Quantity) GreaterThan(p Quantity) bool

func (Quantity) IsNegative

func (t Quantity) IsNegative() bool

func (Quantity) IsPositive

func (t Quantity) IsPositive() bool

func (Quantity) IsZero

func (t Quantity) IsZero() bool

func (Quantity) LessThan

func (t Quantity) LessThan(quantity Quantity) bool

func (Quantity) MarshalJSON

func (t Quantity) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for baseCmd.

func (Quantity) Mul

func (t Quantity) Mul(p Quantity) Quantity

func (Quantity) String

func (q Quantity) String() string

func (Quantity) Sub

func (t Quantity) Sub(p Quantity) Quantity

func (*Quantity) UnmarshalJSON

func (t *Quantity) UnmarshalJSON(decimalBytes []byte) error

type Range

type Range struct{ From, To Date }

Range represents a range of dates.

func NewRange

func NewRange(from, to Date) Range

NewRange creates a new date range. If 'from' is after 'to', they are swapped.

func (Range) Contains

func (r Range) Contains(date Date) bool

Contains return true date is included in the range (boundaries included)

func (Range) Days

func (r Range) Days() iter.Seq[Date]

Days returns an iterator that yields each date within the range, inclusive.

func (Range) Identifier

func (r Range) Identifier() string

Identifier compute a unique identifier for the Range. If the period is defined, use a short insighful name

func (Range) Name

func (r Range) Name() string

Name the period range

func (Range) Period

func (r Range) Period() (p Period, ok bool)

return the period of this range if it's a standard one.

func (Range) Periods

func (r Range) Periods(p Period) iter.Seq[Range]

Periods returns an iterator that yields each sequential range of a given period 'p' that contains at least one day within the original range 'r'.

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

func (r *Review) AssetDividends(ticker string) Money

AssetDividends calculates the dividends received for a single security during the period.

func (*Review) AssetMarketGain

func (r *Review) AssetMarketGain(ticker string) Money

AssetMarketGain calculates the change in a security's value due to price movements during the period.

func (*Review) AssetNetTradingFlow

func (r *Review) AssetNetTradingFlow(ticker string) Money

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

func (r *Review) AssetTimeWeightedReturn(ticker string) Percent

AssetTimeWeightedReturn calculates the time-weighted return for a single security over the review period.

func (*Review) AssetTotalReturn

func (r *Review) AssetTotalReturn(ticker string) Money

AssetTotalReturn calculates the total return for a single security during the period, combining market gains/losses and dividend income.

func (*Review) CashChange

func (r *Review) CashChange() Money

CashChange calculates the net change in the total cash balance during the review period.

func (*Review) CashFlow

func (r *Review) CashFlow() Money

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

func (r *Review) CounterpartyChange() Money

CounterpartyChange calculates the net change in the total counterparty balance during the review period.

func (*Review) CurrencyTimeWeightedReturn

func (r *Review) CurrencyTimeWeightedReturn(currency string) Percent

CurrencyTimeWeightedReturn calculates the time-weighted return for a currency's exchange rate over the review period.

func (*Review) DividendReturn

func (r *Review) DividendReturn() Percent

DividendReturn calculates the return from dividends as a percentage of the starting portfolio value.

func (*Review) Dividends

func (r *Review) Dividends() Money

Dividends calculates the total income received from dividends during the review period.

func (*Review) End

func (r *Review) End() *Snapshot

End returns the snapshot at the end of the review period (taken on `period.To`).

func (*Review) MarketGain

func (r *Review) MarketGain() Money

MarketGain calculates the change in security value due to price movements, isolated from the impact of buying or selling.

func (*Review) Name

func (r *Review) Name() string

func (*Review) NetTradingFlow

func (r *Review) NetTradingFlow() Money

NetTradingFlow calculates the total net cash invested into or divested from all securities during the review period.

func (*Review) PortfolioChange

func (r *Review) PortfolioChange() Money

PortfolioChange calculates the net change in total portfolio value during the review period.

func (*Review) Range

func (r *Review) Range() Range

Range returns the period range of the review.

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

func (r *Review) Start() *Snapshot

Start returns the snapshot at the beginning of the review period (taken on `period.From - 1`).

func (*Review) TimeWeightedReturn

func (r *Review) TimeWeightedReturn() Percent

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

func (r *Review) TotalMarketChange() Money

TotalMarketChange calculates the net change in the total market value of all securities during the review period.

func (*Review) TotalReturn

func (r *Review) TotalReturn() Money

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 NewSecurity(id ID, ticker, currency, description, fetcher string) Security

func (Security) Currency

func (s Security) Currency() string

func (Security) Description

func (s Security) Description() string

Description returns the user-provided description for the security.

func (Security) Fetcher

func (s Security) Fetcher() string

Fetcher returns the fetch URI for the security.

func (Security) ID

func (s Security) ID() ID

ID returns the unique, standardized identifier of the security.

func (Security) Ticker

func (s Security) Ticker() string

Ticker returns the human-friendly ticker symbol of 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

func NewSell(day Date, memo, security string, quantity Quantity, amount Money) Sell

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) Currency

func (t *Sell) Currency() string

func (Sell) Equal

func (t Sell) Equal(other Transaction) bool

func (Sell) MarshalJSON

func (t Sell) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for Sell.

func (*Sell) UnmarshalJSON

func (t *Sell) UnmarshalJSON(data []byte) error

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) Cash

func (s *Snapshot) Cash(currency string) Money

Cash returns the balance of a specific cash account on the snapshot's date.

func (*Snapshot) CashFlow

func (s *Snapshot) CashFlow(currency string) Money

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) Convert

func (s *Snapshot) Convert(amount Money) Money

Convert converts a monetary amount into the default currency.

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

func (s *Snapshot) Counterparties() iter.Seq[string]

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

func (s *Snapshot) Counterparty(account string) Money

Counterparty returns the balance of a specific counterparty account on the snapshot's date.

func (*Snapshot) Currencies

func (s *Snapshot) Currencies() iter.Seq[string]

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

func (s *Snapshot) Dividends(ticker string) Money

Dividends calculates the total income received from dividends for a specific security since inception.

func (*Snapshot) ExchangeRate

func (s *Snapshot) ExchangeRate(currency string) Money

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

func (s *Snapshot) LastMarketDataDate(ticker string) Date

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

func (s *Snapshot) MarketValue(ticker string) Money

MarketValue calculates the market value of a single security on the snapshot's date.

func (*Snapshot) Name

func (s *Snapshot) Name() string

func (*Snapshot) NetTradingFlow

func (s *Snapshot) NetTradingFlow(ticker string) Money

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) On

func (s *Snapshot) On() Date

On returns the date of the snapshot.

func (*Snapshot) Position

func (s *Snapshot) Position(ticker string) Quantity

Position calculates the quantity held of a single security on the snapshot's date.

func (*Snapshot) Price

func (s *Snapshot) Price(ticker string) Money

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

func (s *Snapshot) ReportingCurrency() string

ReportingCurrency returns the reporting currency of the journal associated with the snapshot.

func (*Snapshot) Securities

func (s *Snapshot) Securities() iter.Seq[string]

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

func (s *Snapshot) SecurityDetails(ticker string) (Security, bool)

SecurityDetails finds the declaration for a given ticker.

func (*Snapshot) TotalCash

func (s *Snapshot) TotalCash() Money

TotalCash returns the total cash balance across all currencies, converted to the reporting currency.

func (*Snapshot) TotalCashFlow

func (s *Snapshot) TotalCashFlow() Money

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

func (s *Snapshot) TotalCounterparty() Money

TotalCounterparty returns the total balance across all counterparty accounts, converted to the reporting currency.

func (*Snapshot) TotalCounterpartyIn

func (s *Snapshot) TotalCounterpartyIn(currency string) Money

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

func (s *Snapshot) TotalDividends() Money

TotalDividends returns the total dividends received across all securities, converted to the reporting currency.

func (*Snapshot) TotalMarket

func (s *Snapshot) TotalMarket() Money

TotalMarket returns the total market value of all securities in the portfolio.

func (*Snapshot) TotalMarketIn

func (s *Snapshot) TotalMarketIn(currency string) Money

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

func (s *Snapshot) TotalNetTradingFlow() Money

TotalNetTradingFlow returns the total net trading flow across all securities, converted to the reporting currency.

func (*Snapshot) TotalPortfolio

func (s *Snapshot) TotalPortfolio() Money

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

func (s *Snapshot) VirtualAssetValue(ticker string) Money

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

func (s *Snapshot) VirtualTotalValue() Money

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 NewSplit

func NewSplit(date Date, ticker string, num, den int64) Split

NewSplit creates a new Split transaction.

func (Split) Equal

func (t Split) Equal(other Transaction) bool

func (Split) MarshalJSON

func (t Split) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for Split.

func (*Split) UnmarshalJSON

func (t *Split) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for Split.

func (Split) Validate

func (t Split) Validate(ledger *Ledger) (Transaction, error)

Validate checks the Split transaction's fields.

type SplitInfo

type SplitInfo struct {
	Numerator   int64
	Denominator int64
}

SplitInfo holds the details of a stock 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

func (t UpdatePrice) PricesIter() iter.Seq2[string, decimal.Decimal]

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.

func (UpdatePrice) When

func (t UpdatePrice) When() Date

When returns the date of the 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

func NewWithdraw(day Date, memo string, amount Money) Withdraw

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) Currency

func (t *Withdraw) Currency() string

func (Withdraw) Equal

func (t Withdraw) Equal(other Transaction) bool

func (Withdraw) MarshalJSON

func (t Withdraw) MarshalJSON() ([]byte, error)

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

func (t *Withdraw) UnmarshalJSON(data []byte) error

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.

func (Withdraw) When

func (t Withdraw) When() Date

When returns the date of the transaction.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL