ADR-020: Concentration Lens Default OFF (Concentration Changed to Opt-in)
Date: 2026-06-25 Status: accepted Supersedes/Continues: ADR-013 (trim-concentration-override), ADR-019 (remove solvency auto-fallback, concentration controlled only by lens), ADR-017 (config-via-API provides runtime overrides)
Context
The implicit premise of the concentration lens is: the user has recorded all their net assets into openInvest, so "single asset as X% of holdings" equals "single asset as X% of net worth."
In reality, openInvest holdings are mostly self-selected / watchlists — users only record the assets they want to track, not all their money (e.g., a user's net assets include ¥4M family backup funds, only used as a bankruptcy safety net; the system only sees a ¥220K pocket money account). Under this premise, "holding concentration = risk → suggest reducing position" is wrong for the default user.
Previous rounds have been fixing the concentration algorithm to make it "calculate more correctly":
- ADR-019 / #84: Removed solvency auto-fallback ("sufficient solvency → concentration not risky" reversed CIO at the parse layer, the prompt was unaware, leading to contradictions).
- #89: NaN prices no longer pollute total assets/concentration.
But these were all fixing a feature whose premise is wrong, and new bugs keep emerging. Latest example (2026-06-25): When the gold committee ran overnight, NDQ.AX (ASX/AUD leg) had no fresh quote. Per #89, it was excluded from total_assets_cny → gold concentration was passively calculated from the real ~60% to 91.2% (= gold/(gold+cash), NDQ not in denominator) → triggered an unwarranted TRIM −60000.
Decision
core/config/tunable.py: concentration_lens_enabled default changed from True to False. Concentration is changed from "on by default, everyone must find a way to turn it off" to opt-in:
- Default (False): No position reduction suggestions based on holding concentration —
TRIM_REASON=concentrationunconditionally force-HOLD, Risk/CIO prompt suppresses overweight rules. Other risk dimensions are retained: volatility / drawdown / stop-loss / valuation / stress testing / cash liquidity. - Those who want it (users who have recorded all net assets into the system and genuinely care about portfolio concentration) can explicitly enable it:
/api/configsetverdict.concentration_lens_enabled=true, or envINVEST_VERDICT_CONCENTRATION_LENS_ENABLED=true.
The existing 3-layer disable implementation (agents/risk_officer.py + agents/cio.py prompt soft suppression + core/committee/cio_parse.py Sanity 4 hard force-HOLD) remains unchanged — only the default value is flipped.
Consequences
- Default users will no longer receive "concentration overweight → reduce position" suggestions; they will no longer be misjudged because a watchlist is treated as net assets.
- The #89 NaN-leg concentration miscalculation no longer has a verdict impact for default users (when the lens is off, this number drives no decisions). It is retained only as a known boundary for opt-in users — it can be fixed separately later (missing price legs use last close / average price in the denominator, rather than being entirely excluded), but it is no longer a pressure point requiring repeated patching.
- Behavioral change: Related tests are changed to explicitly enable the lens before testing the on-path;
test_concentration_lens_off_by_default_forces_holdguards the new default.