Implementation design · 001-cash-snapshot
Point-in-time cash-account snapshots
How we will build it.
001-cash-snapshot
Created
Read time
Realise the two-book snapshot as a small, std-only Rust crate: exact i64 cents, a hand-rolled
business-day calendar, and a settlement_date that reads the standard cycle in force on each trade's trade
date. snapshot folds a trade book into IBOR (traded) and ABOR (settled) balances as of an instant. The one
subtlety — and the biggest risk — is the settlement-cycle edition: the cycle is a domain fact that must be grounded in
the corpus and pinned by edition, so a cutover in the rule is a re-grounding, not a code guess.
1 · Principles check
Verify this design respects every principle in v0.1.0. Any violation requires either a principles amendment or a deliberate exception logged below.
| Principle | Compliance | Notes |
|---|---|---|
| P-2 · Small, reversible changes | One crate, one public snapshot entry point; the settlement-cycle edition is data, changeable by re-grounding, not a rewrite. | |
| P-3 · Explicit over implicit | Money is exact i64 cents; a settlement date is always a business day — no floating point, no silent weekend settle. | |
| P-5 · Record the why | D-001..D-006 each cite the §3 grounding row they rest on — a corpus edition for a domain fact, the crate source for the stack pick. | |
| P-9 · Decisions are grounded | Every domain decision is verified against a pinned corpus edition; the one assumed decision (D-005) is a product knob, deliberately un-grounded. | |
| P-7 · Done means verified | The crate ships with 5 passing cargo test cases spanning the cutover and the IBOR/ABOR reconcile — mechanical, not asserted. |
2 · Technical context
- Language(s)
- Rust (2021 edition), standard library only — no external crates.
- Frameworks
- None. A single library crate,
settlement. - Architecture
- Single-module domain library: a
SettlementCalendar, an edition-correctstandard_cycle, and a puresnapshotfold over a trade book. No I/O, no framework. - Storage
- None — the caller owns the trade book and calendar; the crate is pure computation.
- Testing
- Rust
#[cfg(test)]unit tests on fixed fixtures spanning the 2024 cutover;cargo test. - Coverage
- Diff-aware: the settlement and snapshot paths carry direct tests; a change never lowers coverage of the code it touches. Never a single universal %.
- Perf targets
- Not in scope — a snapshot is an in-memory fold over a bounded book; there is no latency SLO in this slice.
- Constraints
- Exact integer cents (no floating point on money); settlement dates are always business days; the settlement cycle is edition-pinned domain data, not a hard-coded constant.
3 · Grounding & evidence
Every design-bearing fact this design rests on, and how it was confirmed — read this before the
approach: it separates what the design knows from what it assumes (per
REQ-LIFECYCLE-006). Status is verified (opened the source
this turn), spike (decidable only by a time-boxed investigation — run it, record the
finding), or assumed (taken as true without verification). A must-tier decision
may not rest on an assumed/unresolved-spike fact unless accepted as a
<spec-risk> (see §8).
| Claim the design rests on | Source | Status | Finding / note |
|---|---|---|---|
| The US standard settlement cycle in force is T+1 — a trade settles one business day after trade date, from the 28 May 2024 compliance date. | KB-0001@2024-05-28 (finance-settlement corpus) |
SEC Rule 15c6-1 shortened the standard cycle to T+1, compliance date 28 May 2024 — the current corpus edition, which supersedes the earlier T+2 edition. Re-grounded from KB-0001@2017-09-05 after the cutover. Grounds D-001. | |
| IBOR is a trade-date (traded-cash) view; ABOR is a settlement-date (settled-cash) view; PBOR is out of scope. | KB-0003@2024-01-15 (finance-settlement corpus) |
Books of record differ chiefly in cash basis and timing: IBOR shows traded cash immediately, ABOR once settled. Grounds D-002. | |
| A snapshot is a balance "as of" an instant; settled cash counts trades whose settlement date is on or before the as-of time. | KB-0004@2024-01-15 (finance-settlement corpus) |
A correct as-of snapshot applies the settlement rule in force on each trade's trade date, not today's. Grounds D-003. | |
| FX settlement risk is on the full principal of the unsettled leg; PvP settlement eliminates principal risk. | KB-0002@2022-07-08 (finance-settlement corpus) |
Report exposure on principal with a PvP flag (BIS CPMI). Grounds D-004. | |
| A 60-second snapshot cache TTL is an acceptable freshness/cost trade-off. | — (product choice) | A local product knob, not a domain fact; deliberately not grounded against the corpus. Grounds D-005. | |
A std-only Rust crate with i64 cents and a hand-rolled calendar realises the whole computation. |
impl/src/lib.rs — settlement_date, snapshot, standard_cycle, T1_EFFECTIVE |
The crate compiles std-only and passes 5 cargo test cases spanning the cutover and the IBOR/ABOR reconcile. Grounds D-006. |
4 · Approach
The crate is one module. Date is a day-count (Howard Hinnant's civil ⇄ days algorithms) so ordering and
business-day arithmetic are cheap and total. SettlementCalendar holds weekends plus a holiday set and offers
is_business_day / add_business_days; settlement_date composes the latter with
standard_cycle(trade_date), which returns the cycle length in force on that trade date. snapshot
folds a trade book into a Snapshot { ibor_cents, abor_cents }: a trade counts toward IBOR when its trade date
is on or before the as-of instant, and toward ABOR when its settlement date is.
The single load-bearing choice is that the cycle length is domain data pinned by edition, not a constant.
standard_cycle reads T+1 on or after the 28 May 2024 compliance date and T+2 before it; that cutover is
grounded against KB-0001@2024-05-28 and exercised by tests that straddle it — never a guess in code.
settlement_date into a two-book Snapshot.5 · Alternatives considered
| Option | Exact money | Zero-dep footprint | Edition control | Total |
|---|---|---|---|---|
| Rust, std-only — chosen | 5 | 5 | 5 | 15 |
| Rust + a date crate (chrono/time) | 5 | 3 | 5 | 13 |
| Python + pandas | 2 | 2 | 3 | 7 |
A date crate would buy calendar conveniences the hand-rolled day-count already covers, at the cost of a dependency and less direct control over the business-day rule. Python's float money basis fails the exact-cents constraint outright. The std-only crate wins on all three axes and keeps the settlement cycle a one-constant, fully-owned edition switch.
6 · Decisions
D-001 · Settlement date from a business-day calendar (T+1)
- Status
Accepted - Context
- FR-001 needs a trade's settlement date. The standard US-equity cycle is T+1 from the 28 May 2024 compliance date, grounded on the §3 corpus row
KB-0001@2024-05-28, which supersedes the earlier T+2 edition — re-grounded here after applying2024-05-28-usd-t1-settlement. - Decision
- Settlement date = trade date + 1 business day (T+1) for trades on or after 28 May 2024, and + 2 (T+2) before, where business days exclude weekends and the calendar's US market holidays. The cycle length is read from
standard_cycle(trade_date), not hard-coded at the call site. - Consequences
- + point-in-time correct and edition-pinned; a cutover is a re-grounding, not a rewrite. − the pinned edition must be re-grounded when the rule changes, or the design drifts from the live corpus (exactly the staleness signal this example demonstrates).
D-002 · Cash basis per book — IBOR traded, ABOR settled
- Status
Accepted - Context
- FR-002 needs two balances from one account. The §3 corpus row
KB-0003@2024-01-15gives the basis: IBOR is trade-date (traded cash), ABOR is settlement-date (settled cash); PBOR is deferred. - Decision
Snapshot { ibor_cents, abor_cents }: IBOR sums trades with trade date ≤ as-of; ABOR sums trades with settlement date ≤ as-of. PBOR is out of scope for this slice.- Consequences
- + one fold yields both books; their difference is cash in flight. − a consumer must not read ABOR as available cash — holds are a later slice.
D-003 · As-of snapshot semantics
- Status
Accepted - Context
- SC-001 and SC-002 require a balance "as of" an instant. The §3 corpus row
KB-0004@2024-01-15fixes the semantics: settled cash counts trades whose settlement date is ≤ the as-of time, under the rule in force on each trade date. - Decision
snapshot(opening, book, as_of, cal)is a pure fold; a trade contributes to ABOR only oncecal.settlement_date(trade_date) <= as_of. Opening balance seeds both books.- Consequences
- + a historical as-of reproduces exactly, because each trade settles under its own edition. − correctness depends on the calendar's holiday set being right for the window.
D-004 · FX exposure on principal with a PvP flag
- Status
Accepted - Context
- FR-003 asks for FX settlement exposure. The §3 corpus row
KB-0002@2022-07-08(BIS CPMI) says settlement risk is on the full principal of the unsettled leg, and PvP eliminates principal risk. - Decision
- For a two-legged trade, report exposure on the principal of the unsettled leg and carry a boolean PvP flag; a PvP-settled trade reports zero principal risk.
- Consequences
- + the exposure number matches how settlement risk is actually measured. − FX is a SHOULD in this slice; the single-currency path ships first.
D-005 · Snapshot refresh interval (60 s TTL)
- Status
Accepted - Context
- FR-004 (MAY) allows a refresh cadence. This is a local product decision — a freshness/cost trade-off, not a domain fact — so it is grounded
assumedand, per the design's grounding rules, a product choice must not manufacture a corpus citation to look verified. - Decision
- Default a 60-second cache TTL for a computed snapshot, product-configurable; below the TTL a cached snapshot is returned, above it the snapshot is recomputed.
- Consequences
- + bounds recomputation cost with a single knob. − a stale-tolerant default; a consumer needing to-the-second freshness sets the TTL to zero.
D-006 · Implementation stack — Rust, std-only
- Status
Accepted - Decision drivers
- Exact integer money · zero external dependencies · direct control of the business-day / edition rule
- Considered options
- Context
- This is a stack pick, not a domain fact, so it is grounded on real code, never the corpus: the §3 row
impl/src/lib.rs— the symbolssettlement_date,snapshot,standard_cycle, andT1_EFFECTIVE— and its 5 passingcargo testcases. - Decision
- Implement as the std-only Rust crate
settlement:i64cents, a hand-rolledSettlementCalendar, and an edition-correctstandard_cycle. - Consequences
- + no supply chain, exact money, one-constant edition control. − the calendar and day-count are ours to maintain and test.
7 · Project structure
Only the new or changed paths, grouped by the single-module boundary the §6 decisions draw.
impl/
Cargo.toml crate: settlement (std-only)
src/
lib.rs Date, SettlementCalendar, standard_cycle, Trade, Snapshot, snapshot() + 5 tests
8 · Risks & mitigations
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| R-1 · The pinned settlement cycle (D-001) goes stale when the standard cycle changes, so the design asserts a rule the live corpus no longer carries. | H | M | Re-ground D-001 against the newer corpus edition on any cutover; the corpus-staleness validate warning makes the drift loud rather than silent. |
| R-2 · The holiday set is wrong for the window, so a settlement date lands on a non-business day. | L | H | is_business_day excludes weekends and holidays; tests pin Memorial Day 2024 across the cutover. |
| R-3 · A consumer reads ABOR as available cash and over-spends against holds not yet modelled. | M | M | Name the two books explicitly; available cash is a deferred slice (spec §3 out-of-scope), documented on the Snapshot type. |
9 · Complexity tracking
Anywhere this design introduces complexity not strictly required by the spec, justify it here.
The day-count and business-day calendar are hand-rolled rather than pulled from a date library. This is deliberate: it removes a dependency, keeps money and dates as plain integers, and makes the settlement cycle a single owned constant that a corpus re-grounding can flip — the whole point of the worked example.
10 · Open questions
None outstanding — every domain decision is grounded against a pinned corpus edition, the stack pick against the real crate, and the one product knob (D-005) is deliberately left as an assumed default rather than an open question.
11 · Change log
- Initial design. Std-only Rust crate: settlement-date from a business-day calendar (D-001, T+2, grounded on KB-0001@2017-09-05), IBOR/ABOR cash basis (D-002), as-of snapshot semantics (D-003), FX exposure + PvP (D-004), a 60 s refresh TTL as a product knob (D-005, assumed), and the std-only Rust stack pick grounded on the real crate (D-006).
- Re-grounded D-001 after applying 2024-05-28-usd-t1-settlement: T+2 → T+1, and its corpus pin from KB-0001@2017-09-05 to KB-0001@2024-05-28 (the current edition), clearing the corpus-staleness warning.