ADR-024: Cash Position Opportunity Cost Rule Changed to Opt-in (Default OFF)
Date: 2026-06-30 Status: accepted Supersedes/Continues: ADR-007 (CIO zero-shot lock-in — this ADR removes its bundled cash_opportunity_cost_rule dead flag), ADR-017 (config-via-API provides runtime overrides), ADR-020 (concentration lens same opt-in paradigm), ADR-023 (honest positioning, not pretending alpha)
Context
The CIO prompt (agents/skills/cio/SKILL.md) contains a section titled "🔥 Cash Position Opportunity Cost Rule (Mandatory, Must Read)":
CONCENTRATION_PCT < 20% → HOLD is not allowed, default is at least
ACCUMULATE, alloc = DRY_POWDER_CNY × 5%~10% (build a small exploratory position). Sole exemption: Macro=risk_off and Risk=high_risk.
Its financial logic is "holding cash and waiting is not free; for every 1% the market rises, you underperform by 1%; a 0% position waiting for a pullback = timing the market." Essentially, it is an aggressive deployment lever — as long as you have ample dry powder and a low position, it forces you into the market.
Two problems:
- It is too aggressive for default users. Same as the concentration lens in ADR-020: openInvest holdings are often self-selected/watchlist items. "Not adding now" could be the completely correct decision (valuation at 99th percentile, downtrend, waiting for a lower entry). Treating HOLD as a "wrong default" deprives the committee of the ability to say "doing nothing is best right now" — and "doing fewer wrong things" is the core of ADR-023's honest positioning.
- It looks like a switch but is actually impossible to turn off.
core/config/locked.pyhascash_opportunity_cost_rule: bool = True, attached toLockedPromptIdentity(ADR-007 locked zone). It looks like a config item — but nowhere in the entire codebase reads it, except for one test asserting it == True. What actually takes effect is the hardcoded prompt text in SKILL.md, which is not connected to any switch.
Real-world impact (2026-06-30, user added ¥10k cash then ran the committee): 510300.SS valuation at 2-year 99th percentile, Quant neutral. CIO was inclined to HOLD, but was forced by this rule to change to ACCUMULATE ¥2,100 — the reason field directly stated: "Cash position opportunity cost rule: CONCENTRATION_PCT 0.9% < 20%, therefore HOLD is not allowed." User verdict: delete this mandatory rule.
Decision
Change it from "hardcoded + dead flag" to a real config-via-API switch, aligning with the ADR-020 concentration lens paradigm:
- Add
verdict.cash_opportunity_cost_rule_enabled(core/config/tunable.py), defaultFalse(rule off). - When off (default):
agents/cio.pyinjects aCASH_OPP_COST_DIRECTIVEsoft-layer directive, explicitly stating "HOLD is legal at any position size / any cash ratio; it is not allowed to force ACCUMULATE or prohibit HOLD solely due to low concentration / ample dry powder; the Verdict options below stating 'ACCUMULATE=100% cash default' and 'HOLD only valid at 20%+' are also void." Whether to add positions is decided purely based on Quant/Macro/Risk signals + valuation/trend evidence. - When on: the directive is an empty string, and the original rule section in SKILL.md takes effect as before (for users who explicitly opt-in to the "low concentration means at least build a small exploratory position" behavior).
- Added to the
API_SETTABLEwhitelist (automatically adjustable via GUI committee config area + skillconfigsubcommand +PUT /api/config, persisted and shared across processes). - Remove the dead flag
cash_opportunity_cost_rulefromlocked.pyand its test assertion (misleading: named like a switch but never read).
Pure prompt soft layer, no deterministic post-processing. cio_parse.py's _force_hold only forces into HOLD, never forces out of HOLD, so turning off the opportunity cost rule requires no hard fallback — simply removing the prompt pressure allows the LLM to freely choose HOLD.
Consequences
- Default behavior change: With low concentration and ample dry powder, the committee is no longer forced to at least ACCUMULATE and can legitimately HOLD. Fork users upgrading for the first time will see more HOLDs — this is expected (aligning with ADR-023: HOLD is not failure, it is discipline).
- Users who want to retain the old aggressive deployment behavior:
config --set verdict.cash_opportunity_cost_rule_enabled true(or enable via GUI committee config area). - Orthogonal to
verdict.risk_profile=aggressive(uptrend HOLD→ACCUMULATE trend-following leverage): that rule governs "trend-following leverage on uptrends," while this rule governs "forced deployment at low positions." Both are off by default and are explicit opt-in aggressive options.
Verification
tests/test_committee_parser.py: Default prompt contains the "opportunity cost rule has been disabled by the user" directive; overrideenabled=Trueremoves the directive and retains the original rule section.tests/test_config.py: All tests pass after removing the dead flag assertion from locked config.