Application map / Investment allocation

How Investment Allocation Works

The reserve-fund allocator: what it recommends, why it is shaped the way it is, and where the running code deviates from this clean description.

The output is advisory. The allocator produces a recommended set of purchases — concrete CDs at named institutions, or treasuries — that a human executes. There is no trading, rebalancing, or brokerage integration. Soft constraints surface as warnings, never hard failures.

1 · Investable cash cash on hand − $100k reserve floor, floored at zero 2 · Bucket cashflows duration boundaries; expenses, revenue, maturities per bucket 3 · Buffer expenses +15% under 12 months · +30% at or beyond 4 · Liquidity needs cumulative buffered net dips below zero → (duration, amount) 5 · Capped greedy fill needs first, shortest up; whatever remains → longest duration 6 · Geometric ladder ½ ¼ ⅛ … down eligible durations; round ↓ to $1,000 lots 7 · Product matching highest yield first; CDs through the FDIC split Purchase recommendation advisory — a human executes; unmatched amounts still reported bounded by construction: no stage can spend more than stage 1's number, so no post-hoc capping or scale-down exists anywhere (see the trap warning)
The seven-stage allocator, from idle cash to an advisory purchase list.

The problem

An HOA holds a reserve fund as idle cash. Idle cash earns nothing, so the excess above a safe operating minimum should be placed in low-risk fixed income — CDs and US treasuries — across a range of maturities. The allocation must satisfy four constraints simultaneously:

  1. Reserve floor. A minimum cash balance always remains uninvested (default $100,000).
  2. Liquidity. Money must be back in cash before it is needed: no allocation may lock funds past the date a net shortfall occurs, given known upcoming expenses and income.
  3. Insurance. Deposit products stay within FDIC limits so no dollar is exposed to bank failure.
  4. Purchasability. Recommendations map to real products at current yields, not abstract durations.

Because future expenses are estimates, the design is conservative by construction: overstated caution should cost a little yield, never solvency.

The pipeline

1 · Investable cash

Cash on hand minus the minimum reserve, floored at zero. Every later stage is bounded by this number by construction — the pipeline never produces a plan that spends more than it, so no post-hoc capping or scale-down step exists anywhere. (This is the design's central property; see the trap warning at the bottom.)

2 · Cashflow bucketing

The candidate durations (CD preset: 3/6/9/12/24/36/48/60 months; treasury: 3/6/12/24/36/60) define date boundaries from the reference date, and the boundaries define consecutive buckets. Every known cashflow — revenue, expense, or maturing holding — is summed into a bucket. Buckets are half-open: an expense lands in the bucket containing its start date.

3 · Buffering — conservatism where the uncertainty lives

Safety margin is applied by inflating expenses, not by shaving the investable amount: each expense bucket is multiplied by (1 + buffer), with a smaller buffer for short horizons where estimates are firmer (default 15% under 12 months) and a larger one for long horizons (30% at or beyond). Expenses are the uncertain quantity, so that is where the conservatism belongs; a flat haircut on investable cash would penalize every allocation equally regardless of when the risk occurs and leave the liquidity analysis itself unprotected.

4 · Liquidity needs — cumulative buffered shortfalls

Walk the boundaries in date order, accumulating buffered net cashflow (revenue plus maturities minus buffered expenses). Whenever the cumulative sum dips below zero, that deficit is a liquidity need: that many dollars must have matured back into cash by that horizon. Emit the pair (duration, amount) and reset the sum to zero. The result is a small list of duration-tagged amounts describing exactly when cash must be liquid.

5 · Capped greedy fill

Fill the liquidity needs in increasing duration order from the investable cash — each fill is min(need, remaining) — and send whatever remains after all needs are met to the longest duration, where yield is highest. Because remaining cash only decreases, the total allocated can never exceed investable cash.

6 · Geometric laddering

A single large allocation at one maturity concentrates reinvestment risk at one date and forfeits interim liquidity, so each raw allocation is redistributed down the ladder of eligible shorter durations with geometric weights: half stays at the original duration, a quarter at the next shorter, an eighth at the next, with the final weight repeated so the weights sum to exactly one. Laddered amounts still sum to the pre-ladder total, preserving the cap. Amounts are then rounded down to $1,000 purchase lots — never to nearest, which is what keeps the cap intact through rounding — with sub-lot residue staying in cash.

7 · Product matching

Each laddered duration amount is matched against the live product catalog: products of the chosen type maturing after the reference date and at or before the duration's target, ordered by yield descending. Treasuries take the single highest-yield match; CDs go through the FDIC split below. If nothing matches, the duration amount is still reported — unmatched — so the human sees the full intended portfolio shape.

FDIC rules (CDs only)

FDIC insurance covers up to $250,000 per depositor per institution. The allocator enforces:

Worked example

$500,000 cash, $100,000 reserve → $400,000 investable. Durations 3, 12, 36 months.

Where the live code deviates from this description

A page written from the clean design alone would be wrong in two respects. Both are load-bearing to what the algorithm actually recommends.

  1. Expenses lag revenue and maturities by one bucket. The live liquidity walk nets, at boundary j: revenues[j] + maturities[j] − (1 + buffer)·expenses[j−1] — expenses come from the prior bucket. Consequences: the first bucket's revenue/maturity is never counted, the last expense bucket is never counted, and the longest-duration boundary is never evaluated for a need (leftover cash lands there via the fill regardless). This reads as an intentional conservative-timing choice — require the cash liquid one bucket before the expense — but it has not been independently confirmed as intended. Treat it as behavior to preserve; do not "fix" it to same-bucket netting without a domain-owner ruling.
  2. The buffer tier is chosen by the next bucket's span, not the expense's own horizon. The walk selects the long-term buffer when the following interval spans ≥ 12 months, and applies it to the lagged expense bucket — so under the default CD preset the 9→12-month expense bucket gets the 30% long buffer despite being nominally "under 12 months". This looks more like an artifact than a design choice; flagged for review rather than relied upon.

The accepted approximation

The greedy fill hands the highest-yield candidates to whichever duration is processed first. The true optimum is a global LP/knapsack optimizer maximizing total yield across all durations subject to the liquidity needs and the FDIC cap. Greedy is the deliberately accepted approximation — simple, auditable, and its yield loss is small at HOA portfolio sizes; the LP is the known upgrade path when sizes justify it.

Deliberately unbuilt

Trap warning. An earlier allocator sized each duration independently from projected cash, shaved investable cash by (1 − buffer), and proportionally scaled everything down when the sum exceeded available cash. It lost on structural grounds: the flat haircut puts conservatism in the wrong place, and post-hoc rescaling transiently recommends more than exists, distorts inter-duration ratios, and caused a real capping bug. Do not resurrect the proportional scale-down when "fixing" allocation totals — it is the known bug pattern, not a fallback. The current design also loads all inputs server-side from the property's rows, so the recommendation cannot disagree with the database.

Where it runs: a vendored, dependency-free backend package behind POST /api/investments/allocations, also consumed by the board packet, the management dashboard, and platform-admin tooling. Constants: $100K reserve, 0.15/0.30 buffers, $250K FDIC limit.