User guide
How to use Investment Portfolio Manager, screen by screen.
Overview
Investments → Overview shows one card per portfolio you may see: NAV, coverage (complete or how many items are not valued), cost of valued holdings, unrealized and realized results, dividend income, settled cash, unsettled amounts, the as-of date and when it was computed, draft events, open discrepancies, imports in progress and accounting work waiting. A card marked Stale was computed before the ledger, policy or prices changed: press Recalculate. Nothing on the overview is live market data.
Recording events
Portfolios → Transaction ledger → New. Choose the type, portfolio, broker account, instrument (for trades, opening positions and dividends), trade date, quantity and price; add charges (commission, exchange, clearing, stamp duty, withholding…) on the Charges tab. Gross and net are filled in for you and checked on confirmation:
- Buy: net = gross + charges. Sell and dividend: net = gross − charges.
- Gross must equal quantity × price within the portfolio tolerance.
- The settlement date defaults from the exchange calendar.
Confirm checks the whole history, not only the new event: a sale that would oversell (or a back-dated sale that would make a later sale oversell) is refused with the reason; so is a purchase the cash cannot fund.
Types: buy, sell, deposit (contribution), withdrawal (distribution), fee, cash dividend, opening position, opening cash balance, and — through the Transfer wizard only — cash or security transfers between portfolios or accounts (both legs together; securities keep their cost).
Correcting
Confirmed events cannot be edited or deleted. Reverse / correct:
- Reverse only — adds a linked reversal; the original is marked reversed.
- Reverse and open a corrected draft — also opens an editable copy linked to the original (version + 1).
Both need a reason and are in the audit trail. A reversal that would leave a later event invalid is refused until that event is dealt with.
Valuation
Recalculate today (portfolio) or Valuation → Run valuation (any date) stores a valuation run: holdings (trade-date and settled quantity), average cost, cost, price and its date and status, market value, unrealized result, translated value; cash, receivables and payables per currency; NAV and completeness notes. Print holdings produces the PDF.
Reports and exports
Reporting → Reports & exports: holdings & valuation, transaction ledger, cash movements (opening + movements = closing, per currency), realized gains, dividends — and, with the bridge, accounting bridge status; and with the importer the reconciliation lists. Output PDF, CSV or Excel. Every report states company, portfolio, currency, as-of date or period, policy, completeness and when/by whom it was generated. CSV/Excel exports cannot carry formulas.
Reporting → Performance: Modified Dietz return and investment gain for one or several portfolios; "not available" with the reason when it cannot be computed honestly.
Statements
See broker-profile-guide.md: import, review, duplicate decisions, commit, results download.
Reconciliation
Reconciliation → Reconciliation runs (or Reconcile on a batch):
| Kind | Compares |
|---|---|
| Statement transactions | statement rows with ledger events for the account and period |
| Statement positions | a position snapshot with ledger holdings (trade or settled basis) |
| Statement cash | a cash snapshot with ledger settled cash per currency |
| Accounting (with the bridge) | ledger events with proposals and posted entries; GL balances with the ledger |
| Ledger integrity | drafts left unconfirmed, transfers whose cost moved |
Each discrepancy has a category (missing, in ledger but not in evidence, duplicate candidate, amount difference, mapping problem, settlement timing, policy difference, unresolved), expected/actual/difference, owner, age, suggested action and drill-down to the statement row, the event and the journal entry. Ambiguous matches are kept ambiguous with all candidates.
States: new → investigating → resolved or accepted difference (both need a reason) → reopened if needed. A decision never changes a ledger event. To correct the ledger, Propose adjustment creates a draft event with evidence that an administrator must confirm.
Accounting (bridge)
Accounting → Generate proposals (events or valuation, up to a date) → review proposals → Create draft entries → an accountant posts them in Accounting. Re-running generation reuses proposals; a posted one that no longer matches the ledger is flagged, not rewritten. Reversed events produce linked reversing entries under Odoo's lock-date rules.
How every figure is computed
These are the product's management calculation conventions. They are not universal financial-reporting rules and not tax rules. The accounting bridge has its own, separately configured treatment (see accounting-policy-guide.md); where the two differ, reports label which one they show.
All arithmetic runs in Python Decimal (technovision_investment_core/services/engine.py). Amounts are stored as PostgreSQL NUMERIC(24,6); results are rounded half-up to 6 decimals for storage and to the currency's decimals for display. The browser never computes a figure.
1. The ledger
Every figure is derived from confirmed ledger events. A reversed event and its reversal are both excluded (they cancel). Events are applied in the order
trade date → type priority → trade time → id
where the type priority on one day is: opening balances, deposits and incoming cash transfers, incoming security transfers, purchases, dividends, fees, sales, outgoing security transfers, withdrawals and outgoing cash transfers. Money and shares therefore arrive before they leave on the same day.
2. Cost — moving weighted average
| Quantity | Definition (default management policy: purchase fees in cost) |
|---|---|
| Purchase cost | gross amount + purchase fees + purchase taxes |
| Average cost | remaining cost ÷ remaining quantity (display only) |
| Allocated cost of a sale | remaining cost × quantity sold ÷ quantity held |
| Full exit | the entire remaining cost; the position is left at exactly 0 |
| Realized result | net sale proceeds − allocated cost |
| Net sale proceeds | gross − sale fees − sale taxes (policy "sale fees reduce proceeds") |
The allocation uses the total cost, not a rounded average, so no rounding error accumulates across partial sales.
Policy options (effective-dated, per portfolio):
- Purchase fees in cost off → purchase cost = gross; fees are reported as expenses.
- Sale fees reduce proceeds off → realized = gross − allocated cost; sale fees are reported as expenses.
- Sell against settled holdings → a sale may not exceed holdings that have settled by the trade date.
- Cash control: block (default) → an event that would leave trade-date cash negative is refused: borrowing and margin are not supported.
Only moving weighted average is offered. FIFO is not implemented, so it is not selectable. The cost method cannot change once a portfolio has events.
3. Trade date, settlement and cash
- Holdings move on the trade date. Settled holdings move on the settlement date.
- Cash moves on the settlement date. Between trade and settlement: a sale or a dividend is an unsettled receivable; a purchase, fee or withdrawal is an unsettled payable.
- A dividend is income on its entitlement (trade) date and cash on its payment (settlement) date.
- Each amount exists exactly once at any date: sale proceeds are a receivable until settlement and cash afterwards, never both.
Settlement dates come from the statement when printed; otherwise exchange (or portfolio) settlement lag in trading days, skipping the exchange's weekend and holidays.
4. Valuation and NAV
Market value = trade-date quantity × valid price Unrealized result = market value − remaining management cost NAV = Σ market value (valued lines)
- settled cash + unsettled receivables − unsettled payables
A valid price is the latest unadjusted observation dated on or before the valuation date (a close is preferred over a last trade on the same day). A future price is never used. Corporate-action-adjusted series are stored but never value holdings, which are recorded unadjusted.
- A price older than the portfolio's staleness threshold is used and flagged stale.
- No price → the line is not valued: excluded from NAV, listed with its cost, and the valuation is marked incomplete. It is never valued at zero.
5. Currencies
Each figure is kept in its native currency. Translation to the portfolio's reporting currency uses the latest invest.fx.rate observation on or before the date (direct or inverse pair). A missing rate is missing: the line is not translated and the valuation is incomplete. FX is 1 only between a currency and itself. Realized and income totals are translated at the valuation-date rate (closing-rate translation), and reports say so.
6. Transfers
A security transfer carries its cost: the outgoing leg's allocated cost becomes the incoming leg's cost. Nothing is realized by moving shares. In a combined view of several portfolios, transfers whose two legs are both inside the selection are excluded from flows, so internal transfers cannot inflate a combined return.
7. Performance — Modified Dietz
R = (V1 − V0 − ΣF) / (V0 + Σ Wᵢ·Fᵢ) Wᵢ = (E − dᵢ) / (E − S)
- V0 and V1 are end-of-day NAVs on S and E. A flow dated S is already inside V0; flows dated after S and up to E count; a flow on E weighs 0.
- External flows: deposits, withdrawals, opening balances and transfers in/out (securities at market value on the transfer date).
- Dividends, fees and taxes are return, not flows. Deposits are never counted as profit.
- The investment gain V1 − V0 − ΣF is reported separately from the percentage.
- Not available (never 0) when either valuation is incomplete, a flow cannot be valued, the period is empty, or the denominator is not positive.
- No benchmark comparison is shown.
8. Reference fixture
Management policy with purchase fees in cost:
| Step | Cash | Shares | Cost |
|---|---|---|---|
| Deposit 10,000 | 10,000 | 0 | 0 |
| Buy 100 @ 20, fee 10 | 7,990 | 100 | 2,010 |
| Buy 50 @ 22, fee 5 | 6,885 | 150 | 3,115 |
| Sell 60 @ 25, fee 6 (proceeds 1,494, allocated 1,246) | 8,379 | 90 | 1,869 |
Realized 248. At a price of 24: market value 2,160, unrealized 291, cash plus stock 10,539, total gain 539 (= 248 + 291).
This fixture is tested three ways: through the ORM (technovision_investment_core/tests/test_arithmetic.py), in the pure engine with no database, and end to end by importing the sample statement (technovision_investment_suite/tests/test_end_to_end.py).