# Portfolio accounting: a runnable example

Version 1.0.0, October 15, 2026. Vesper Applied.

Companion to https://vesperapplied.com/research/total-return-index-not-executable-portfolio/

This small teaching example separates a dividend receivable from available cash. It compares a cash-funded portfolio with an index that notionally reinvests dividends at the ex-date close. Every price, date, and event is synthetic. There are no market-data downloads, account connections, or third-party packages.

## Run it

Install Python 3.10 or later if needed. Extract the ZIP and open a terminal in the `portfolio-accounting-v1` folder. On Windows, use:

```text
python accounting.py --check
python -m unittest -v
python accounting.py
```

On macOS or Linux, use `python3` in place of `python` if needed. The first command should print:

```text
PASS: 7 synthetic scenarios, 15 rows match expected.csv.
```

The second command runs 11 tests. The third prints the session-by-session CSV. To save a new output file, run `python accounting.py > results.csv`. This replaces `results.csv` if that file already exists.

The check compares your output with supplied expectations. It does not connect to a market-data service or independently validate investment results. The tests also check hand-derived balances and accounting properties, so the output comparison is not the only check.

## What the files contain

- `accounting.py`: the calculation and command-line interface.
- `scenarios.json`: seven editable synthetic scenarios.
- `expected.csv`: expected output, rounded to four decimal places.
- `test_accounting.py`: accounting and invalid-input checks.
- `README.md`: instructions, assumptions, and worked expectations.
- `LICENSE.txt`: the MIT license for this example and its synthetic fixtures.

## Read the results

`equity = cash + shares * close + receivable`. All money amounts are USD. `shares` may be fractional. `fees` reports only that session's fees, already deducted from cash. The index starts at 100 for every scenario. It is a dimensionless benchmark, not a portfolio dollar balance. Do not compare its level with a portfolio's dollar value as a return comparison when their starting values or exposure differ.

| Scenario | Hand-derived check |
| --- | --- |
| Hold until payment | On the ex-date, $98 of shares + $2 receivable = $100, with $0 available cash. At a later $107.80 close, equity is $109.80. The close-reinvested index is 100 x 107.80 / 98 = 110. Payment moves $2 from receivable to cash without changing equity. |
| Sell on the ex-date | Selling at $98 leaves $98 cash + $2 receivable = $100. The later $108 close does not affect the sold position. The index reaches 110 under its separate convention. |
| Buy on the ex-date | The buyer spends $98 for one share and has no entitlement to the earlier holder's $2 dividend. |
| Wait for available cash | After selling, $98 cash cannot fund a $100 purchase even though equity includes a $2 claim. When payment arrives, $100 cash can fund that purchase. |
| Fund purchase fee | A 1% fee and $100 cash permit 100 / 1.01 = $99.00990099... of holdings and $0.990099... in fees. No cash is borrowed. |
| Split after entitlement | A later 2:1 split changes one $98 share into two $49 shares. The already established $2 claim stays $2. |
| Split on the ex-date | Two $49 shares with a $1 dividend per post-split share produce $98 of shares + $2 receivable = $100. The index factor is 2 x (49 + 1) / 100 = 1. |

The tests check the values before output rounding. Exact fractions represent the calculations; the CSV alone rounds to four decimal places. Multiplying a rounded share quantity by a price may therefore differ slightly from the displayed share value.

## Change an assumption

Copy `scenarios.json` to `my-scenarios.json`. For example, move the first scenario's `cash_available` from January 7 to January 8. Run:

```text
python accounting.py --input my-scenarios.json
```

The final January 7 row will now retain a $2 receivable and $0 cash. Add a January 8 session to see delivery. Changing inputs should change the results. `--check` always compares with the original supplied `expected.csv`, so it should fail when a change affects the output. Review your own arithmetic before adopting new expected results.

Each scenario has a name, initial shares and cash, a previous closing price, a fee rate, and a list of sessions. Write quantities and rates as decimal strings, such as `"0.01"` for a 1% fee. All values must be nonnegative; prices and split ratios must be positive. The fee rate must be below 1.

Each session requires `date`, `open`, and `close`. Dates must increase strictly. Optional fields:

- `split`: new shares per old share; defaults to `"1"`.
- `dividend`: gross amount per post-split share. A nonzero value marks an ex-date and requires `cash_available`.
- `cash_available`: the explicitly assumed date when that dividend can fund an opening trade. It must be on or after the ex-date. It is not inferred from an issuer payable date.
- `action`: `hold`, `sell_all`, `buy_cash`, or `buy_notional`. Defaults to `hold`.
- `notional`: positive dollar value of shares to purchase, required only for `buy_notional`. Fees are added to this amount. `buy_cash` instead sizes the purchase to include fees within available cash.

Before each opening trade, the example applies splits to carried shares, establishes dividend claims from those shares, and delivers claims whose availability date has arrived. It then executes the specified opening action and values the portfolio at the close. A rejected order leaves holdings and cash unchanged after that session's corporate actions and deliveries. Payment can occur after the portfolio has sold all shares. Claims unpaid at the end remain in equity.

Sessions are explicitly supplied observations, not an exchange calendar. If an availability date falls between supplied sessions, delivery is reflected at the next supplied session. Prices are inputs; the code does not predict an ex-date price drop. Actions are also prescribed inputs, not a trading strategy. The example does not test whether a decision used information available at the time.

## Scope and review

This is newly written educational code for selected arithmetic examples in the September 2026 note. It is not the original private reference implementation, a reproduction of its historical verification suite, or a reproduction of the separate LSI audit. The article's original publication date has not changed.

The modeled portfolio holds one security, fractional long shares, and ordinary gross cash dividends. It has no borrowing, interest, taxes, spreads, slippage, liquidity limits, settlement accounting, due bills, special distributions, cash-in-lieu, or impaired claims. Sale proceeds are assumed immediately available. A real account may have different constraints. The example does not derive security identity, entitlement rules, or cash availability from real-world records.

AI tools assisted with implementation, tests, and writing. The checks are internal and have not received independent human review. A useful outside review would challenge the event ordering, try to break the funding checks, and independently calculate the supplied scenarios.

Report an error to hello@vesperapplied.com. Include the article URL, example version, scenario, expected behavior, and observed output. Do not send account credentials or private vendor data. Substantive corrections will be recorded in the article's revision history, with a new version when the download changes.

## Sources

The formulas and initial numerical examples come from the linked Vesper Applied note. Nasdaq's ex-date page provides external context for event dates: https://www.nasdaqtrader.com/Trader.aspx?id=nasdaq-ex-date . It does not validate this code or the synthetic cash-availability assumptions.
