# Anchor-bound instrument mapping and model-series activation

Use this reference when a managed portfolio has a confirmed total/position snapshot and must be continued with modelled daily values through existing market-price, FX, valuation-writer, and scheduler paths.

## 1. Resolve canonical instruments without guessing

Build one row per confirmed position with:

- source name and confirmed position date;
- primary identity (ISIN or equivalent authoritative identifier);
- quantity;
- exact listing/venue;
- trading currency;
- existing canonical instrument ID, if any;
- provider symbol and provider market code;
- decision: `secure`, `review_required`, or `blocked`.

Rules:

1. The authoritative identifier is primary. A ticker or fund name alone never authorizes a mapping.
2. Re-read the confirmed snapshot identifier exactly; do not reuse a visually similar identifier from an existing catalog row.
3. When several listings exist, bind venue and trading currency to statement evidence plus a price-level reconciliation. A statement value converted into the portfolio currency is not evidence that the traded listing itself uses that currency.
4. Provider venue codes may differ from human labels. Store both the concrete listing identity and the exact code returned by the approved provider; validate the pair explicitly.
5. Reuse one existing canonical instrument when its authoritative identifier matches. Insert a new canonical row only when none exists, and prove zero duplicate identifiers afterward.
6. Require exactly one active model-price mapping per confirmed position. Existing review-only/user-confirmed candidates may coexist, but multiple active `mapped` rows are ambiguous and block execution.
7. Never force the observed set to an expected count. A mismatch is a stop condition with an explanation.

## 2. Prove anchor and position provenance

Before provider calls or writes, require:

- one complete confirmed position snapshot;
- the expected source-declared position count equals the physical rows;
- one authoritative identifier per row;
- snapshot date compatible with the official total anchor;
- explicit cash components, or a documented residual-cash derivation;
- source components reconcile to the official control total within the repository's existing tolerance;
- no unresolved external deposits/withdrawals after the anchor.

The official total is a control/anchor, not an additive component. Model arithmetic is:

`sum(quantity × exact-day price × exact-day FX) + evidenced anchor cash + confirmed post-anchor net external flows`

Do not add the official total to positions and cash. Unknown internal trades remain unknown; do not invent rebalancing.

## 3. Run a production-shaped no-write rehearsal

A useful two-stage rehearsal is:

1. **Direct provider dry-run:** query every candidate listing for the bounded interval, preserve actual provider date/timestamp, currency, FX date, and quality, and compute the daily CHF values without touching production.
2. **Temporary online database copy:** insert only the proposed canonical mappings into the copy, run the real price/FX/model service on it, and verify schema compatibility, provider-market validation, exact-date storage, source activation, and writer output.

Hash or sentinel the productive database before and after the direct dry-run. The copy rehearsal must use the deployed schema and production runtime dependencies, but its writes stay confined to the copy.

Dry-run output includes per instrument and day:

- provider symbol/venue;
- actual price and price date;
- provider timestamp;
- FX rate and FX date;
- quantity and CHF value;
- quality/reason code.

Then report securities total, evidenced cash, post-anchor cashflows, model total, difference from the last official anchor, and the latest common fully valued trading day.

## 4. Choose the common market day honestly

- Requested date, provider price date, and valuation date remain distinct.
- On weekends/holidays, use existing carry-forward/business-day semantics and expose the real price date.
- Fetch all position quotes before persisting the total. If actual quote dates differ across listings, no common complete day exists; block the valuation.
- Require exact-day FX for every non-base currency on that common day.
- Never mint a new-day point from stale rows. Keep the previous successful model value with its original date.
- A daily source may legitimately report no new point on a weekend while still displaying the last complete modelled value.

## 5. Activate atomically where required, phase-honestly otherwise

After all stop gates pass:

1. Create and verify a private SQLite online backup before writes; verify the resulting file mode is `0600`, then run backup/source integrity and FK checks.
2. Persist exactly the approved canonical instruments and active mappings in one transaction with an aggregate audit.
3. Fetch and validate the complete day set before storing prices/FX.
4. Write only the authorized bounded model interval after the anchor; never write on/before the anchor and never expand into a general historical backfill.
5. Mark every value modelled/provisional and bind its source identity/fingerprint to the anchor date, positions, prices, FX, cash basis, and post-anchor flows.
6. Replay the model writer separately and require `valuation_stored=0` plus zero new snapshot/run/audit rows.
7. Run the source through the existing source-isolated dispatcher. To prove independence, hash protected crypto/other-source tables before and after the managed-portfolio-only worker rather than manually invoking unrelated providers.

Idempotency claims are scoped:

- model-writer replay proves valuation idempotency;
- provider refresh may update fetched/provenance timestamps or resolve existing quality alerts without inserting a new price/FX row;
- report inserted rows and updated existing rows separately.

## 6. Close the public API/UI contract

The managed-portfolio performance surface should expose only rendered allowlisted fields:

- last confirmed value and date;
- latest modelled value and actual model date;
- net external cashflows since the anchor;
- anchor date;
- price/FX quality and actual price/FX date;
- next scheduled run;
- a statement that the next official document will refine/reconcile the model.

Keep positions, identifiers, provider rows, raw provenance, and fingerprints out of the ordinary dashboard.

UI rules:

- confirmed series is solid;
- modelled series is dashed and starts from the preceding official anchor;
- modelled status is visibly provisional;
- a weekend-blocked current preview may fall back only to the latest stored modelled value and its real date;
- a missing current-day point must not turn an existing model into `unknown` or `confirmed`;
- rendering must cause zero provider calls.

When adding public response fields, update all exact-key contract tests as part of the same candidate before opening the PR. Run the focused HTTP response-model test in addition to the service test; a strict allowlist test may otherwise be the sole full-CI failure.

## 7. Minimum release evidence

Record:

- mapping counts by decision class;
- confirmed snapshot/anchor date and reconciliation;
- provider names;
- dry-run/model total and common day;
- exact model interval and point count;
- last confirmed and last modelled values;
- per-table inserted/deleted counts and updates to existing rows;
- writer and source-dispatcher replay results;
- source gate and next local-time run;
- protected-source hashes;
- integrity, FK, schema, duplicate-identifier count;
- API smoke, browser labels/line styles, console errors, and zero render-time provider calls;
- exact merged/deployed SHA and final successful CI run when code changed.

Stop after this bounded activation. Do not continue into unrelated providers, transaction-history reconstruction, general backfill, trading, or a next sprint.
