From 98c41c37896a41c5f2f15ed187e55ab617735342 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 14:31:13 +0800 Subject: Add rebalance first slice (3d-7) First vertical slice for portfolio rebalancing. Domain gains RebalancePolicy: target-allocation validation (non-empty, per-code shares positive two-decimal, sum exactly 100); diff computation over the union of target and held codes (held without target = full exit) producing BUY amount diffs and SELL unit diffs priced at the current valuation NAV, clamped to available units, with no orders when a weight already balances; and the deterministic idempotency key set (rebalance:{plan}:{date}:{code} plus confirm/redeem counterparts). Rebalance plans persist as rebalance_plans + rebalance_targets with rebalance_idempotencies under the shared replay/conflict contract. ExecuteRebalancePlan snapshots cash plus holdings valued at each position's latest valuation NAV, then walks the diffs SELL-first through the same Create/Confirm pipeline used by manual orders, redemptions and SIP (nothing bypasses confirmation; a held code without a valuation is skipped with an explicit marker; insufficient cash/units fail the period visibly instead of silently trimming). API exposes POST/GET /funds/{id}/rebalance/plans and POST .../plans/{id}/execute. The frontend adds an 08/REBALANCE panel with target editing (two codes, sum must equal 100), a plan list with a manual execute button and post-run outcome rows; executing refreshes orders/positions so the new orders are immediately visible. Domain tests cover split validation, diff orders, exit/clamp logic, key determinism; API tests cover create/execute end-to-end; Web tests cover decoding and stale-execution protection. --- src/FundLab.Api/Persistence.fs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/FundLab.Api/Persistence.fs') diff --git a/src/FundLab.Api/Persistence.fs b/src/FundLab.Api/Persistence.fs index d64fe98..6c0374a 100644 --- a/src/FundLab.Api/Persistence.fs +++ b/src/FundLab.Api/Persistence.fs @@ -612,6 +612,14 @@ type FundRepository(connectionString: string) = created_at timestamptz NOT NULL DEFAULT now() ); + CREATE TABLE IF NOT EXISTS rebalance_idempotencies ( + idempotency_key text PRIMARY KEY, + request_hash text NOT NULL, + plan_id uuid NOT NULL REFERENCES rebalance_plans(id), + fund_id uuid NOT NULL REFERENCES funds(id), + created_at timestamptz NOT NULL DEFAULT now() + ); + CREATE TABLE IF NOT EXISTS rebalance_targets ( plan_id uuid NOT NULL REFERENCES rebalance_plans(id), instrument_code text NOT NULL REFERENCES instruments(code), -- cgit v1.2.3