Skip to content

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:

  1. 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.
  2. It looks like a switch but is actually impossible to turn off. core/config/locked.py has cash_opportunity_cost_rule: bool = True, attached to LockedPromptIdentity (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), default False (rule off).
  • When off (default): agents/cio.py injects a CASH_OPP_COST_DIRECTIVE soft-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_SETTABLE whitelist (automatically adjustable via GUI committee config area + skill config subcommand + PUT /api/config, persisted and shared across processes).
  • Remove the dead flag cash_opportunity_cost_rule from locked.py and 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; override enabled=True removes the directive and retains the original rule section.
  • tests/test_config.py: All tests pass after removing the dead flag assertion from locked config.