diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-21 14:31:13 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-21 14:31:13 +0800 |
| commit | 98c41c37896a41c5f2f15ed187e55ab617735342 (patch) | |
| tree | a57cffc9e9147124af12b79e58ccf083bb12ab2f /src/FundLab.Api/App.fs | |
| parent | 336d5e5312eb6fa69ccc1fd875e930a067ff8143 (diff) | |
| download | fund-lab-98c41c37896a41c5f2f15ed187e55ab617735342.tar.gz | |
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.
Diffstat (limited to 'src/FundLab.Api/App.fs')
| -rw-r--r-- | src/FundLab.Api/App.fs | 7 |
1 files changed, 6 insertions, 1 deletions
diff --git a/src/FundLab.Api/App.fs b/src/FundLab.Api/App.fs index ac4c174..4ae00de 100644 --- a/src/FundLab.Api/App.fs +++ b/src/FundLab.Api/App.fs @@ -544,7 +544,12 @@ module App = | Some code, Some percentText -> match tryDecimal "targetPercent" percentText with | Ok percent -> - let targetRow: RebalancePolicy.TargetAllocation = { InstrumentCode = code; TargetPercent = percent } + let targetRow: RebalancePolicy.TargetAllocation = + { + RebalancePolicy.TargetAllocation.InstrumentCode = code + TargetPercent = percent + } + rows.Add(targetRow) | Error message -> failure <- Some message | _ -> failure <- Some "each target needs instrumentCode and targetPercent" |
