Back to Wealthfolio

Activity Types Reference

docs/activities/activity-types.md

3.3.022.5 KB
Original Source

Activity Types Reference

This document provides a comprehensive reference for Activity types used in Wealthfolio. Understanding these types is essential for tracking portfolio movements and for creating activities programmatically through addons or CSV imports.

Overview

Wealthfolio uses a closed set of 14 canonical activity types. Each activity

  • Cash Balance: The cash position in the account (by currency)
  • Asset Quantity: The number of shares/units held
  • Cost Basis: The original cost of holdings for gain/loss calculations
  • Net Contribution: Total money contributed to/withdrawn from the portfolio (affects TWR performance)

Summary Table

TypeCategoryCash ImpactHoldings ImpactCost BasisNet ContributionRequired Asset
BUYTrading-(qty × price + fee)+quantity+costNo changeYes
SELLTrading+(qty × price - fee)-quantity-cost (FIFO)No changeYes
SPLITTradingNo changeAdjustedPer-share adjustedNo changeYes
DEPOSITCash+(amount - fee)N/AN/A+amountNo
WITHDRAWALCash-(amount + fee)N/AN/A-amountNo
TRANSFER_INTransfer+amount or +quantity+quantity (asset)Preserved/set+amount (account scope)Optional
TRANSFER_OUTTransfer-amount or -quantity-quantity (asset)Removed (FIFO)-amount (account scope)Optional
DIVIDENDIncome+(amount - fee)No changeNo changeNo changeYes
INTERESTIncome+(amount - fee)No changeNo changeNo changeOptional
CREDITIncome+(amount - fee)No changeNo changeDepends on subtypeNo
FEECharge-amountNo changeNo changeNo changeOptional
TAXCharge-amountNo changeNo changeNo changeOptional
ADJUSTMENTOtherVariesVariesVariesNo changeYes (required)
UNKNOWNOtherNo auto impactNo auto impactNo auto impactNo changeOptional

Activity Categories

Trading Activities

BUY

Purpose: Purchase of a security or other asset.

ImpactDescription
CashDecreases by (quantity × unit_price) + fee in activity currency
HoldingsIncreases quantity; new lot created with cost basis
Cost BasisIncreases by (quantity × unit_price) + fee
Net ContributionNo change (internal reallocation of cash to asset)

Required Fields: asset, quantity, unit_price, currency Optional Fields: fee, amount

Example: Buy 10 shares of AAPL at $150 with $5 fee

  • Cash: -$1,505 USD
  • Holdings: +10 AAPL shares
  • Cost Basis: +$1,505

SELL

Purpose: Disposal of a security or other asset.

ImpactDescription
CashIncreases by (quantity × unit_price) - fee in activity currency
HoldingsDecreases quantity; lots reduced using FIFO
Cost BasisDecreases by cost basis of sold lots (FIFO matching)
Net ContributionNo change (internal reallocation of asset to cash)

Required Fields: asset, quantity, unit_price, currency Optional Fields: fee, amount

Note: Realized gain/loss = proceeds - cost basis of sold lots.


SPLIT

Purpose: Stock split or reverse split adjustment.

ImpactDescription
CashNo change
HoldingsQuantity adjusted by split ratio
Cost BasisPer-share cost adjusted (total cost unchanged)
Net ContributionNo change

Required Fields: asset, amount (split ratio, e.g., 2 for 2:1) Optional Fields: metadata.split_ratio (e.g., "2:1"), quantity (unused, use amount for ratio)

Example: 2-for-1 split of 100 shares at $200/share

  • Before: 100 shares @ $200 = $20,000 cost basis
  • After: 200 shares @ $100 = $20,000 cost basis

Cash Activities

DEPOSIT

Purpose: Incoming funds from outside Wealthfolio (external source).

ImpactDescription
CashIncreases by amount - fee in activity currency
HoldingsN/A
Cost BasisN/A
Net ContributionIncreases by amount (new capital entering portfolio)

Required Fields: amount, currency Optional Fields: fee

TWR Impact: External flow - creates a sub-period boundary for TWR calculation.


WITHDRAWAL

Purpose: Outgoing funds to an external destination.

ImpactDescription
CashDecreases by amount + fee in activity currency
HoldingsN/A
Cost BasisN/A
Net ContributionDecreases by amount (capital leaving portfolio)

Required Fields: amount, currency Optional Fields: fee

TWR Impact: External flow - creates a sub-period boundary for TWR calculation.


Transfer Activities

TRANSFER_IN

Purpose: Move cash or assets into this account.

ScenarioCash ImpactHoldings ImpactNet Contribution
Cash transfer+amountN/A+amount (account scope)
Asset transfer-fee only+quantity (new lot)+cost_basis (account scope)

Required Fields:

  • Cash: amount, currency
  • Asset: asset, quantity, unit_price, currency

Optional Fields: fee, metadata.flow.is_external

Flow Behavior:

Scopeis_external = false (default)is_external = true
Account+net_contribution+net_contribution
PortfolioNo change (nets to zero with TRANSFER_OUT)+net_contribution

Use Cases:

  • Default: Transfer between Wealthfolio accounts (cost basis preserved)
  • External: Adding holdings from outside the portfolio (gifts, inheritance, external brokerage)

TRANSFER_OUT

Purpose: Move cash or assets out of this account.

ScenarioCash ImpactHoldings ImpactNet Contribution
Cash transfer-(amount + fee)N/A-amount (account scope)
Asset transfer-fee only-quantity (FIFO)-cost_basis (account scope)

Required Fields:

  • Cash: amount, currency
  • Asset: asset, quantity, currency

Optional Fields: fee, metadata.flow.is_external

Flow Behavior: Same as TRANSFER_IN but with opposite sign.

Use Cases:

  • Default: Transfer between Wealthfolio accounts
  • External: Removing holdings from portfolio (gifts, donations, external transfer)

Income Activities

DIVIDEND

Purpose: Cash dividend paid into the account.

ImpactDescription
CashIncreases by amount - fee in activity currency
HoldingsNo change (unless DRIP subtype)
Cost BasisNo change
Net ContributionNo change (income, not new capital)

Required Fields: asset, amount, currency Optional Fields: fee, quantity (for per-share tracking)

Subtypes: See Dividend Subtypes section.


INTEREST

Purpose: Interest earned on cash or fixed-income positions.

ImpactDescription
CashIncreases by amount - fee in activity currency
HoldingsNo change (unless STAKING_REWARD subtype)
Cost BasisNo change
Net ContributionNo change (income, not new capital)

Required Fields: amount, currency Optional Fields: asset, fee

Subtypes: See Interest Subtypes section.


CREDIT

Purpose: Cash-only credit such as refunds, rebates, or bonuses.

ImpactDescription
CashIncreases by amount - fee in activity currency
HoldingsN/A
Cost BasisN/A
Net ContributionDepends on subtype (see below)

Required Fields: amount, currency Optional Fields: subtype

Net Contribution by Subtype:

SubtypeNet ContributionRationale
BONUS+amountNew capital (sign-up bonus, referral bonus)
REBATENo changeReduced trading cost, not new capital
REFUNDNo changeReversal of existing fee, not new capital
(default)No changeInternal adjustment

Fee & Tax Activities

FEE

Purpose: Stand-alone brokerage or platform fee not tied to a trade.

ImpactDescription
CashDecreases by amount (or fee field) in activity currency
HoldingsNo change
Cost BasisNo change
Net ContributionNo change

Required Fields: amount or fee, currency Optional Fields: asset (for asset-specific fees)

Common Subtypes:

  • MANAGEMENT_FEE: Advisory/management fee
  • ADR_FEE: ADR custody fee
  • INTEREST_CHARGE: Margin interest

TAX

Purpose: Tax paid from the account (withholding, CGT, etc.).

ImpactDescription
CashDecreases by amount in activity currency
HoldingsNo change
Cost BasisNo change
Net ContributionNo change

Required Fields: amount, currency Optional Fields: asset (for asset-specific taxes)

Common Subtypes:

  • WITHHOLDING: Dividend withholding tax
  • NRA_WITHHOLDING: Non-resident alien withholding

Other Activities

ADJUSTMENT

Purpose: Non-trade correction or transformation (usually no cash movement).

ImpactDescription
CashTypically no change
HoldingsMay change
Cost BasisMay change
Net ContributionNo change

Required Fields: asset, varies by use case Optional Fields: metadata with adjustment details

Use Cases:

  • Option expiring worthless
  • Return of capital basis adjustment
  • Merger/spinoff compiler input
  • Corporate action adjustments

Note: This is a flexible type for non-standard corrections. Specific handling depends on the subtype and metadata.


UNKNOWN

Purpose: Unmapped or unrecognized activity type requiring user review.

ImpactDescription
AllNo automatic impact until classified

Behavior: Activities imported with unrecognized types are marked as UNKNOWN and flagged for review (needs_review = true). Users should manually reclassify using activity_type_override or delete these activities.


Subtypes

Subtypes provide semantic variations of activity types without schema changes. The compiler expands these into canonical activity postings.

Dividend Subtypes

SubtypeDescriptionExpansion
DRIPDividend Reinvestment Plan - dividend automatically reinvestedDIVIDEND + BUY
QUALIFIEDQualified dividend (tax classification)DIVIDEND (pass-through)
ORDINARYOrdinary dividend (tax classification)DIVIDEND (pass-through)
RETURN_OF_CAPITALReturn of capital (reduces cost basis)DIVIDEND (special handling)
DIVIDEND_IN_KINDDividend paid in different asset (e.g., spinoff shares)DIVIDEND + TRANSFER_IN

DRIP Expansion

Stored Activity:

json
{
  "activity_type": "DIVIDEND",
  "subtype": "DRIP",
  "asset": { "id": "AAPL" },
  "amount": 100, // dividend cash amount
  "quantity": 0.5, // shares received
  "unit_price": 200 // reinvestment price
}

Compiled Postings:

  1. DIVIDEND: amount = $100 (income recognition)
  2. BUY: quantity = 0.5, unit_price = $200 (share acquisition)

Net Cash Effect: ~$0 (dividend received equals purchase cost)


DIVIDEND_IN_KIND Expansion

Stored Activity:

json
{
  "activity_type": "DIVIDEND",
  "subtype": "DIVIDEND_IN_KIND",
  "asset": { "id": "PARENT_CO" },
  "amount": 250,
  "quantity": 10, // shares of spinoff received
  "unit_price": 25, // FMV at receipt
  "metadata": {
    "received_asset_id": "SPINOFF_CO"
  }
}

Compiled Postings:

  1. DIVIDEND: amount = $250 (income recognition from PARENT_CO)
  2. TRANSFER_IN: asset = SPINOFF_CO, quantity = 10, unit_price = $25 (receive spinoff shares)

Interest Subtypes

SubtypeDescriptionExpansion
STAKING_REWARDCrypto staking reward received as tokensINTEREST + BUY
LENDING_INTERESTInterest from securities lendingINTEREST (pass-through)
COUPONBond coupon paymentINTEREST (pass-through)

STAKING_REWARD Expansion

Stored Activity:

json
{
  "activity_type": "INTEREST",
  "subtype": "STAKING_REWARD",
  "asset": { "id": "ETH" },
  "quantity": 0.01, // ETH received
  "unit_price": 2000, // FMV at receipt
  "amount": 20 // value = 0.01 * 2000
}

Compiled Postings:

  1. INTEREST: amount = $20 (income recognition)
  2. BUY: quantity = 0.01, unit_price = $2000 (token acquisition)

Net Cash Effect: $0 (income equals acquisition cost)


Credit Subtypes

SubtypeDescriptionNet Contribution
BONUSSign-up/referral/promotional bonusExternal flow (+)
REBATETrading rebate (maker rebate, volume rebate)Internal flow (no change)
REFUNDFee correction/reversalInternal flow (no change)

Metadata Structure

Activities support a metadata JSON field for additional context:

json
{
  "flow": {
    "is_external": true
  },
  "received_asset_id": "SPINOFF_CO",
  "split_ratio": "2:1",
  "source": {
    "broker": "Schwab",
    "original_type": "REI"
  }
}

Key Metadata Fields

FieldTypeUsed ByDescription
flow.is_externalbooleanTRANSFER_IN/OUTMarks transfer as crossing portfolio boundary
received_asset_idstringDIVIDEND_IN_KINDAsset ID received (different from paying asset)
split_ratiostringSPLITHuman-readable split ratio (e.g., "2:1")
source.brokerstringAllOriginal broker name
source.original_typestringAllRaw activity type from provider

Activity Status

Each activity has a status that controls whether it affects calculations:

StatusDescriptionAffects Calculations
POSTEDFinalized activityYes
PENDINGAwaiting settlement or confirmationNo
DRAFTUser-created draft, not yet finalizedNo
VOIDCancelled or reversed (soft delete)No

Note: Only POSTED activities are compiled and processed by the holdings calculator.


Activity Type Override

Users can override the activity type using activity_type_override without modifying the original activity_type. This is useful for:

  • Correcting misclassified imports
  • Mapping UNKNOWN types to canonical types
  • Preserving original provider classification while using correct semantics

The effective_type() method returns the override if set, otherwise the original type.


Activity Type Groups

Trading Types

BUY, SELL, SPLIT

Income Types

DIVIDEND, INTEREST

Cash-Only Types (never have an asset)

DEPOSIT, WITHDRAWAL, FEE, TAX, CREDIT

Note: TRANSFER_IN/TRANSFER_OUT can be cash OR asset depending on whether quantity/price is provided. INTEREST can have an optional asset (e.g., bond interest).


Form Field Requirements

TypeRequired Fields
BUYAsset, Quantity, Unit Price, Currency
SELLAsset, Quantity, Unit Price, Currency
DIVIDENDAsset, Amount, Currency
INTERESTAmount, Currency
DEPOSITAmount, Currency
WITHDRAWALAmount, Currency
TRANSFER_INAmount (cash) or Asset+Quantity+Unit Price (asset), Currency
TRANSFER_OUTAmount (cash) or Asset+Quantity (asset), Currency
FEEAmount or Fee, Currency
TAXAmount, Currency
SPLITAsset, Amount (split ratio)
CREDITAmount, Currency
ADJUSTMENTVaries

Workflow Recommendations

Simple (Holdings-Only)

For quick onboarding when only tracking portfolio value:

  1. Use TRANSFER_IN with is_external = true to add existing positions
  2. Use DEPOSIT to set initial cash balance
  3. Adjust as needed with TRANSFER_IN/OUT

Full (Transaction-Level)

For precise IRR, cash-flow, and tax analytics:

  1. Seed account with DEPOSIT
  2. Record every BUY, SELL, DIVIDEND, INTEREST
  3. Use TRANSFER_IN/OUT for inter-account moves (default internal)
  4. Use TRANSFER_IN/OUT with is_external = true for external moves
  5. Log expenses via FEE and TAX

Best Practices

  1. Use DEPOSIT/WITHDRAWAL for external cash flows - These properly track net contributions for performance calculations.

  2. Use TRANSFER_IN/OUT for inter-account moves - Default behavior preserves cost basis and nets to zero at portfolio level.

  3. Mark external transfers explicitly - Set metadata.flow.is_external = true when crossing portfolio boundary.

  4. Use subtypes for semantic variations - Instead of custom types, use subtypes (e.g., DIVIDEND with subtype DRIP).

  5. Include fees in the activity - Fees are automatically factored into cost basis and cash calculations.

  6. Set currency explicitly - Always specify the activity currency for proper multi-currency handling.

  7. Use activity_type_override for corrections - Preserves original classification for audit purposes.