Vai al contenuto

πŸ“… Asset Events

Asset events represent significant occurrences that affect an asset's price or generate distributions. They live in the asset domain, not the portfolio/transaction domain.

πŸ“‹ Event Types

  Event Type Effect on Price Who Generates Description
πŸ’° DIVIDEND Price drops by event value (ex-date) Provider (yfinance, justetf) or manual Cash distribution from equity/ETF
πŸ’΅ INTEREST Price drops by event value Scheduled Investment (generate_interest) or manual Interest payment from debt/loan. Resets accrued interest
πŸ“Š PRICE_ADJUSTMENT Algebraic change (+/βˆ’) Provider or manual Non-cash value change (write-down, haircut)
βœ‚οΈ SPLIT Changes quantity, not total value Provider or manual Stock/unit split
🏁 MATURITY_SETTLEMENT Final capital return Scheduled Investment (generate_interest) Asset reaches maturity β€” no further calculations

πŸ—ƒοΈ Database Model

erDiagram
    ASSET ||--o{ ASSET_EVENT : "has events"
    ASSET_PROVIDER_ASSIGNMENT ||--o{ ASSET_EVENT : "generates"

    ASSET_EVENT {
        int id PK
        int asset_id FK "β†’ assets.id"
        date date "Event date"
        enum type "DIVIDEND, INTEREST, PRICE_ADJUSTMENT, SPLIT, MATURITY_SETTLEMENT"
        decimal value "Monetary value"
        string currency "ISO 4217"
        int provider_assignment_id FK "nullable β€” NULL = manual"
        text notes "nullable"
    }

No UniqueConstraint on (asset_id, date, type) β€” auto-generated and manual events can coexist on the same date and type.

Indexes: (asset_id, date), (asset_id, type, date), (provider_assignment_id).


πŸ”„ Dedup Strategy

The sync layer uses _upsert_asset_events() to persist events from providers. The dedup logic differentiates between auto-generated and manual events:

πŸ€– Auto-Generated Events (provider_assignment_id IS NOT NULL)

During sync, all existing events with the same provider_assignment_id are deleted, then the new events are inserted. This ensures:

  • Re-syncing replaces stale events with fresh ones
  • Events from different providers for the same asset are independent
  • Events from different sync runs of the same provider are cleanly replaced
# Simplified logic in _upsert_asset_events()
DELETE FROM asset_events
WHERE asset_id = :asset_id
  AND provider_assignment_id = :provider_assignment_id

INSERT INTO asset_events (...)
VALUES (...new_events...)

βœ‹ Manual Events (provider_assignment_id IS NULL)

Manual events are never deleted by the sync process. They survive provider re-syncs, provider changes, and bulk refreshes. Only explicit user deletion removes them.


πŸ”§ Auto-Generated Events

πŸ“ˆ Yahoo Finance: Dividends & Splits

During sync, the Yahoo Finance provider generates:

  • DIVIDEND events from ticker.dividends β€” ex-dividend dates with the per-share payout value and currency.
  • SPLIT events from ticker.splits β€” split dates with the split ratio as the event value (e.g., 4.0 for a 4:1 split).

Both are inserted via the standard _upsert_asset_events() pipeline, keyed by provider_assignment_id.

πŸ” JustETF: Dividends from Chart Data

During sync, the JustETF provider parses dividend data from load_chart() response. Distribution dates and amounts are extracted and stored as DIVIDEND events.

πŸ“Š Scheduled Investment: generate_interest

When a schedule period has generate_interest = True, the Scheduled Investment provider auto-generates:

  1. INTEREST events at each maturation date within the period
  2. After each INTEREST event, accrued interest resets: total_interest = 0, event_adjustment = 0 β†’ value returns to initial_value

The maturation frequency is controlled by maturation_frequency on each interest rate period (DAILY, WEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL).

🏁 MATURITY_SETTLEMENT

Auto-generated when:

  • The schedule ends and generate_interest = True on the last period
  • Or late interest is configured with generate_interest = True

After settlement, the engine is "off" β€” get_current_value() returns the settlement value for all future dates.


πŸ“ Event Impact on Pricing

The Scheduled Investment engine's pricing formula incorporates events:

price(d) = initial_value + accrued_interest - Ξ£(INTEREST events) + Ξ£(PRICE_ADJUSTMENT events)
  • Interest is always calculated on initial_value (not running value)
  • INTEREST events subtract from the running price (coupon paid out)
  • PRICE_ADJUSTMENT events are algebraic (can add or subtract)
  • After MATURITY_SETTLEMENT, no further calculations occur