Functional Weave
Code in Python

invest.portfolio-weights@1.0.0

README.md

1,774 bytes · view raw

# invest.portfolio-weights

Each holding's weight in a portfolio, the weight it is meant to have, and how
far it has drifted, in basis points (10000 = 100%) and in money. It is the
first step of any rebalancing policy: "rebalance when anything drifts more
than 5 percentage points" is `maxAbsDriftBasisPoints > 500`.

## Why it is shaped this way

- **Integer arithmetic throughout.** Values are `Money` in minor units and
  every ratio is computed from them exactly (with 128-bit or arbitrary-size
  integers inside, so a portfolio of billions does not overflow), then
  rounded once.
- **Each weight is rounded on its own**, half-up to a whole basis point. The
  weights therefore need not add up to exactly 10000: three equal holdings
  are 3333 bp each. Forcing them to sum (largest remainder) would move one
  holding's weight by a basis point it does not have, and the drift of that
  holding would be wrong. If you need weights that sum for a pie chart, use
  `money.allocate` on the values.
- **Drift is computed from the exact values**, (value x 10000 - target x
  total) / total, and rounded once, half away from zero. At an exact half
  basis point this can differ by one from `weightBasisPoints -
  targetBasisPoints`, which rounds twice.
- **`driftValue` is the money to sell (positive) or buy (negative)** to hit
  the target exactly, before any whole-unit or minimum-trade constraint;
  `invest.rebalance` applies those.

## Edge cases

- Targets must sum to exactly 10000. A holding may have a target of 0
  (something to sell out of) and a holding may be worth 0 (something to buy).
- A portfolio worth nothing has no weights and is an error, as are negative
  values (short positions are out of scope), duplicate ids, mixed currencies
  and an empty list.