11 KiB
Tax Metrics — Existing Transaction and Dividend Data Model
Owner: Tharun Swaminathan Iteration: 1 — Design & Data Modelling Date: September 15, 2026
Objective
The purpose of this review was to understand how Ghostfolio currently stores and handles transactions, especially dividend transactions, before adding any tax-related functionality.
Since the tax feature will later include withholding tax, FIFO capital gains, average-cost calculations, tax lots, yearly summaries, and exports, it is important to first understand the existing data model instead of creating a separate structure that does not match the rest of Ghostfolio.
No implementation changes were made as part of this task.
Current Persistence Model
Ghostfolio stores portfolio transactions using the Prisma Order model in
prisma/schema.prisma.
Even though the database model is called Order, the rest of the application
usually refers to these records as activities.
The Order model already contains the main information needed to describe a
portfolio transaction.
| Field | Current purpose |
|---|---|
id |
Unique identifier for the activity |
accountId |
Account associated with the activity |
comment |
Optional user comment |
createdAt |
Time the database record was created |
currency |
Currency used by the activity |
date |
Date of the financial activity |
fee |
Fee associated with the activity |
quantity |
Number of units or shares |
symbolProfileId |
Asset associated with the activity |
tags |
Tags attached to the activity |
type |
Type of financial activity |
unitPrice |
Price or value per unit |
updatedAt |
Last time the record was updated |
userId |
User who owns the activity |
The currently supported activity types are:
BUYDIVIDENDFEEINTERESTLIABILITYSELL
This means the tax calculations should build on the existing activity model where possible instead of introducing a completely separate transaction model.
Dividend Representation
Ghostfolio does not currently have a separate database table or entity just for dividends.
A dividend is stored as a normal activity where:
type = DIVIDEND
The activity still uses fields such as quantity, unitPrice, currency,
date, and fee.
For example, one of the existing Ghostfolio import samples contains the following dividend activity:
- Asset: Microsoft (
MSFT) - Quantity: 5 shares
- Unit price: 0.62 USD
- Type:
DIVIDEND - Fee: 0
- Currency: USD
- Date: November 17, 2021
Using the existing quantity and unit-price values, the dividend amount represented by this activity is:
5 × 0.62 = 3.10 USD
This gives the tax feature a natural starting point for determining the dividend value. In the later withholding-tax design, this value can be used when defining the relationship between gross dividend, withholding tax, and net dividend.
Existing Fee Field
Ghostfolio already has a fee field on the activity model.
The same field is available when an activity is created, and the current DTO requires it to be a non-negative number.
However, the current model does not define this field specifically as dividend withholding tax. It is a general activity fee.
Because of this, the tax extension should not simply reinterpret fee as
withholding tax.
A brokerage fee and dividend withholding tax represent different pieces of financial information and may both exist on the same activity.
For the later withholding-tax design, these values should remain conceptually separate:
- Gross dividend
- Activity fee
- Withholding tax
- Net dividend
The exact withholding fields will be designed in the Sep 16 task. This review
only establishes that the existing fee field should keep its current meaning.
API and DTO Flow
The current Ghostfolio activity flow already provides a path that can be extended later for tax-related information.
At a high level, the flow is:
Prisma Order → CreateOrderDto / UpdateOrderDto → Activities backend service → Activity API response
The existing CreateOrderDto already validates important transaction fields
such as:
- currency
- date
- fee
- quantity
- symbol
- activity type
- unit price
The API activity interface then extends the stored Order with additional
calculated information used by the application.
This means the tax feature should reuse this existing flow where possible. Creating a second, independent dividend transaction API would add unnecessary duplication and make the system harder to maintain.
Currency Handling
Currency is already part of Ghostfolio's activity model.
The activity response also includes calculated values such as:
feeInAssetProfileCurrencyfeeInBaseCurrencyunitPriceInAssetProfileCurrencyvaluevalueInBaseCurrency
This is important for the tax feature because tax values may eventually need to be reported both in the activity currency and the user's base currency.
Instead of creating a separate currency-conversion method only for tax, the future implementation should follow Ghostfolio's existing conversion approach where practical.
The exact withholding-tax currency fields are not being decided in this task. That will be part of the Sep 16 schema design.
Transaction Ordering
Transaction ordering is especially important for the later FIFO capital-gains calculation.
During the code review, Ghostfolio's existing activity handling was found to use the activity date as the main chronological value.
This gives us the primary ordering value needed for tax calculations:
activity date
However, FIFO also needs a deterministic rule when two transactions have the same date or timestamp.
The activity model contains both an id and createdAt, but this review does
not yet define which should be treated as the official secondary tax ordering.
This should be explicitly decided in the FIFO specification rather than leaving the calculation dependent on accidental database ordering.
For now, this is recorded as an open design question for the Sep 17 FIFO task.
Import Compatibility
Ghostfolio already supports importing activities, including dividend transactions.
This matters because future tax functionality cannot assume that every dividend was manually entered by the user.
For example, an imported dividend may contain the basic dividend amount but may not contain withholding-tax information.
The tax design should therefore be able to handle at least two situations:
- A dividend where withholding information is available.
- A dividend where withholding information is not available.
Missing withholding information should not make an otherwise valid historical dividend unusable.
The exact validation and default behavior will be defined when the withholding schema is designed.
Existing Tests
Ghostfolio already contains tests and sample data involving dividend transactions.
These are useful because future tax tests should follow the project's existing testing style instead of building a completely different testing approach.
One useful example is
portfolio-calculator-msft-buy-with-dividend.spec.ts, which already covers a
Microsoft purchase together with a dividend. The existing import sample also
contains an MSFT dividend activity.
For future tax calculations, similar tests can be extended to verify exact numeric results for cases such as:
- dividend withholding
- multiple purchases followed by a sale
- partial FIFO lot usage
- multiple tax lots
- same-date activities
- rounding
- missing withholding information
No new tests were written during this task because Iteration 1 is currently focused on design and data modelling.
Design Conclusions
Based on the existing Ghostfolio implementation, the tax feature should extend the current activity model rather than create a separate transaction system.
The main conclusions from this review are:
-
Dividends are already represented as normal activities using
type = DIVIDEND. -
The existing
Ordermodel provides most of the transaction information needed by future tax calculations. -
The existing
feefield should remain separate from withholding tax. -
Gross dividend, withholding amount, and net dividend should remain distinguishable rather than being combined into one value.
-
Existing currency-handling patterns should be reused where possible.
-
Imported and historical dividends may not always contain withholding information, so the future design needs to handle missing values safely.
-
FIFO will need an explicitly documented ordering rule, especially when multiple activities have the same date.
These conclusions will be used as the starting point for the remaining tax design tasks in Iteration 1.
Open Questions
The following questions were identified during this review and will be resolved in later Iteration 1 tax tasks.
Withholding Tax
- Should the withholding amount be stored directly?
- Should a withholding rate also be stored?
- If both amount and rate are available, which one should be treated as the source value?
- Should withholding be optional for older or imported dividend records?
- How should withholding amounts be represented across different currencies?
FIFO and Tax Lots
- What secondary ordering rule should be used when activities share the same timestamp?
- How should stock splits affect tax-lot quantities and cost basis?
- What information needs to be stored to trace a sale back to the acquisition lots that were consumed?
Calculation Precision
- Where should rounding occur?
- Should calculations use the activity currency first and convert afterward, or use base-currency values during the calculation?
These questions are intentionally left open here because they belong to the specific design tasks scheduled later in Iteration 1.
Next Task
The next tax task is scheduled for September 16:
Design the withholding-tax field schema for dividend transactions.
The Sep 15 review provides the baseline for that work.
The next design will define the withholding-tax inputs and fields, including their types, optional/required behavior, validation rules, currency meaning, and relationship to gross and net dividend values.
No withholding-tax implementation will be added yet because Iteration 1 is the design and data-modelling phase.
Relevant Files Reviewed
The main Ghostfolio files reviewed for this task were:
prisma/schema.prismalibs/common/src/lib/dtos/create-order.dto.tslibs/common/src/lib/interfaces/activities.interface.tsapps/api/src/app/activities/activities.controller.tsapps/api/src/app/activities/activities.service.tsapps/api/src/app/portfolio/calculator/roai/portfolio-calculator.tsapps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.tstest/import/ok/sample.json
These files were used to understand the current transaction and dividend data model, API flow, currency handling, and existing dividend behavior. No source-code changes were made during this review.