# Deploy-the-Fund Knobs — CLI-configurable capital + risk, per-sleeve allocation — Design

**Date:** 2026-07-16
**Status:** Design. A focused UPGRADE of the merged capacity-aware allocator — not a new system.
**Author:** jgrusewski (+ Claude)
**Builds on:** `docs/superpowers/specs/2026-07-15-capacity-aware-capital-allocator-design.md` (merged) and
`docs/superpowers/specs/2026-07-15-crypto-honest-reference-pipeline-design.md` (merged 20437d7).

## 1. Problem

The nightly allocation deploys only ~$8,840 of the $100k paper equity (≈9% — 91% cash). Two structural
causes, both confirmed by an in-cluster smoke:

1. **The marginal-prune (leave-one-out) gate keeps only the volatile `positioning` sleeve** and drops the
   diversified `bybit_4edge` book: `survivors_after_marginal_prune:[positioning]` (the book adds no marginal
   Sharpe — it *contains* positioning plus lower-Sharpe drag sleeves).
2. **The conservative 12% vol-target de-levers `positioning` hard**: its honest full-history vol is ~34% →
   `K = kelly 0.5 × target_vol 0.12 / 0.34 ≈ 0.18`, base capped at `max_strategy_weight 0.5` → deployed
   weight ≈ 0.088.

Compounding this: the fund's **capital and risk knobs are hardcoded defaults** — `paper_capital` in
`config.py` and `target_vol` / `kelly_fraction` in `allocation_policy.py`. Changing them requires an env-var
edit + redeploy. A small, actively-managed, growing fund needs to (a) tune its capital + risk appetite live
via CLI, and (b) allocate across the individual capacity-bounded crypto sleeves rather than double-counting a
book that overlaps a sleeve it already deploys.

## 2. Goal

Two coupled changes, paper scope:

- **Fund-config knobs (CLI-settable, single SSOT):** the fund's `capital`, `target_vol`, and `kelly_fraction`
  become a single persisted DB record, set via `fxhnt fund-config`, read by the nightly allocation — live, no
  redeploy, not hand-editable.
- **Per-sleeve crypto allocation:** the allocation set switches from the `bybit_4edge` book (double-counted
  with the `positioning` sleeve) to the individual crypto sleeves (`positioning`, `xsfunding`, `unlock`), each
  sized within its own honest capacity ceiling. The book stays a tracked *reference* forward track, not an
  allocation target.

The forward gate (live-deploy switch) and the `capital_ceiling` human gate are unchanged. This is a minimal,
SSOT-respecting upgrade — **no new allocation system**.

## 3. What already exists (verified 2026-07-16) — reuse, do not rebuild

- `allocation_ingest.record_allocation(dsn, equity, *, store, spread_bps, unlock_events)` allocates over
  `deploy_sids = {sid : tier == "deploy"} | repo.promoted_strategy_ids()`, honest-sizes via
  `allocation_honest_inputs.honest_allocation_inputs` when `store` is injected (else nav fallback), applies the
  killswitch on the forward record, de-funds decayers, and writes `StrategyAllocationRow`.
- `allocation_engine.compute_allocation` does eligibility → LOO marginal-prune → inverse-vol (risk-parity)
  base capped at `max_strategy_weight` → killswitch → ex-ante vol-target `K` (kelly × target_vol /
  full-history-vol, CVaR-floored, max_leverage-capped). `target_capital` converts to dollars with per-strategy
  capacity ceilings + the `capital_ceiling` gross cap.
- `allocation_honest_inputs.honest_allocation_inputs` maps each deploy sid → its honest-reference series +
  capacity ceiling. Today `_CRYPTO_DEPLOY_SIDS = {"positioning", "bybit_4edge"}`; `_deploy_series` special-cases
  those two; every other sid gets `equity_default_ceiling` and no series. `_sleeve_series` already computes ANY
  sleeve's honest series (it is called per-sleeve inside `_book_series`).
- `config.py`: `paper_capital = 100_000`, `capital_ceiling_paper = 1_000_000` (the $35k figure is
  `capital_ceiling_live` — the LIVE gate, not paper). `allocation_policy.py`: `AllocationPolicy(target_vol
  0.12, kelly_fraction 0.5, max_leverage 1.5, max_strategy_weight 0.5, capital_ceiling 35_000 default,
  min_history_days 60, ...)`; `make_policy()` picks the paper/live ceiling from settings;
  `ALLOCATION_POLICY_VERSION = 3`.
- `cockpit_forward` (Dagster asset, `assets.py:1046`) is where the nightly allocation runs:
  `equity = get_settings().paper_capital` → `record_allocation(...)`, wrapped in a graceful try/except that
  falls back to the nav path on any store failure.
- The registry (`registry.py`) drives both the allocation set (`tier`) and the cockpit grouping. `bybit_4edge`
  is `tier: deploy`; `positioning` is `tier: deploy`; `xsfunding` / `unlock` are `tier: research`;
  `bybit_4edge_exec` / `bybit_4edge_levered` are already `tier: research`. `multistrat` uses
  `promote_on_pass: True` to auto-flip research→deploy on gate PASS (`forward_ingest.evaluate_promotions`).

So the plumbing, honest-sizing seam, killswitch, capacity ceilings, and $ conversion are DONE. This spec adds a
config SSOT + read path, and re-points the crypto allocation from the book to its sleeves.

## 4. Design

### 4a. `fund_config` — the single SSOT for the tunable knobs

A **single-row** table (id fixed to 1), migration-created:

```sql
CREATE TABLE IF NOT EXISTS fund_config (
    id             INTEGER PRIMARY KEY CHECK (id = 1),
    capital        DOUBLE PRECISION NOT NULL,
    target_vol     DOUBLE PRECISION NOT NULL,
    kelly_fraction DOUBLE PRECISION NOT NULL,
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

A small module `application/fund_config.py`:

- `@dataclass(frozen=True) FundConfig(capital: float, target_vol: float, kelly_fraction: float)`.
- `read_fund_config(dsn) -> FundConfig` — reads row id=1. **Graceful fallback**: if the table or row is
  absent, returns `FundConfig(capital=settings.paper_capital, target_vol=AllocationPolicy().target_vol,
  kelly_fraction=AllocationPolicy().kelly_fraction)` — byte-identical to today's behaviour, so the nightly
  never breaks on a fresh DB (mirrors the existing store-fallback pattern in `cockpit_forward`).
- `write_fund_config(dsn, *, capital=None, target_vol=None, kelly_fraction=None)` — **upsert with partial
  override**: reads the current effective config, overlays only the provided fields, writes row id=1. So
  `set --capital 250000` changes only capital and leaves vol/kelly as they were.
- **Validation** (fail loud on nonsense — every input validated at the boundary): `capital > 0`,
  `0 < target_vol <= 1.0`, `0 < kelly_fraction <= 1.0`. Reject with a clear message, do not silently clamp.

### 4b. `fxhnt fund-config` CLI (typer, `cli.py`)

- `fxhnt fund-config show` — prints the effective config (capital, target_vol, kelly_fraction) and whether it
  came from the DB row or the settings fallback (so the operator can see "is this the default or a set value").
- `fxhnt fund-config set [--capital FLOAT] [--target-vol FLOAT] [--kelly FLOAT]` — validates, upserts, prints
  the new effective config. At least one flag required (a no-op `set` errors).

### 4c. Wire the nightly to read `fund_config`

In `cockpit_forward` (`assets.py`), replace `equity = get_settings().paper_capital` with
`cfg = read_fund_config(dsn); equity = cfg.capital`, and pass the risk knobs down so `record_allocation`
builds its `AllocationPolicy` with `target_vol = cfg.target_vol`, `kelly_fraction = cfg.kelly_fraction` (all
other policy fields unchanged, via `make_policy()` then a dataclass `replace`). `record_allocation` gains an
optional `policy: AllocationPolicy | None = None` parameter (default `None` → `make_policy()`, byte-identical
to today); `cockpit_forward` passes the fund-config-derived policy. Both the honest path and the nav-fallback
path use the same policy.

### 4d. Per-sleeve allocation — resolve the book/sleeve overlap

Three coordinated changes:

1. **Registry (`registry.py`):**
   - `xsfunding`: `tier: research` → **`promote_on_pass: True`** (NOT a hard flip to deploy). It enters the
     allocation when its reconciliation gate PASSes — respecting the forward gate as the deploy switch, exactly
     as `multistrat` does. Same for **`unlock`**.
   - `bybit_4edge`: `tier: deploy` → **`tier: research`**. The book becomes a tracked *reference* aggregate
     (it already sits alongside its `bybit_4edge_exec` / `bybit_4edge_levered` research twins), no longer an
     allocation target. This removes the positioning/book double-count. `positioning` stays `tier: deploy`.
   - Net deploy set today: `{positioning}` + any of `{unlock, xsfunding}` whose gate has PASSed. As each
     sleeve gate passes, the allocation gains that sleeve — a gated per-sleeve rollout.

2. **Provider (`allocation_honest_inputs.py`):** generalize the crypto mapping so ANY crypto sleeve is
   honest-sized (not just `positioning`):
   - `_CRYPTO_DEPLOY_SIDS = frozenset({"positioning", "xsfunding", "unlock"})` (drop `bybit_4edge` — it is no
     longer in the deploy set; `_book_series` / `_BOOK_SLEEVES` stay for the reference report, unused by the
     allocation path).
   - `_deploy_series(store, sid, ...)`: for `sid in {"positioning", "xsfunding", "unlock"}` →
     `_sleeve_series(store, sid, ...)` (the existing per-sleeve engine); else `{}`. The `sid == "bybit_4edge"`
     book branch is removed from the allocation path.

3. **No execution change today.** Both rebalancers are SUSPENDED (no live crypto creds). The per-sleeve
   allocation writes per-sleeve `StrategyAllocationRow`s (paper/display sizing). When crypto execution is later
   armed, `execute-bybit` must size the book as `Σ(sleeve target_dollars)` over the deploy sleeves — a
   documented **follow-on**, out of scope here (nothing executes today, so no live risk).

### 4e. Seed the initial `fund_config` row (migration data step)

Insert row id=1: `capital = 100_000` (current), `target_vol = 0.20` (the chosen risk appetite — moderately
conservative), `kelly_fraction = 0.5` (unchanged). Idempotent (`ON CONFLICT (id) DO NOTHING`) so a re-run does
not clobber a value the operator has since set via CLI.

## 5. Falsification gate — does per-sleeve actually deploy more? (the honest check)

Per-sleeve is **not guaranteed** to raise deployment. The LOO marginal-prune gate — correctly — drops sleeves
that do not add marginal Sharpe, and the diversifier-hunt (2026-07-15) already found **`unlock` fails the LOO
gate** (co-crash / immaterial). So the deploy set may honestly collapse toward `{positioning}` or
`{positioning, xsfunding}`. This spec does NOT weaken or remove the marginal gate to force diversification —
co-crashers *should* be pruned.

**Predetermined gate (run in-cluster after implementation, before any go-live):**

- **G1 (survival):** report `survivors_after_marginal_prune` for the per-sleeve deploy set on the honest
  series. Expected: `{positioning}` and, if it adds marginal Sharpe, `{xsfunding}`; `unlock` expected pruned.
- **G2 (deployment):** report the resulting deployment % of equity at `target_vol = 0.20`. Compare against the
  positioning-alone baseline (8.84% at 12% vol). The honest lever ranking is: **vol-target 0.12→0.20 raises
  deployment regardless of survival; per-sleeve raises it further ONLY if xsfunding survives LOO.**
- **G3 (no reactive de-sizing):** confirm the ex-ante `K` is still computed from full-history vol (never
  trailing) — the anti-reactive invariant is untouched.
- **Verdict rule:** if the deploy set collapses to `{positioning}` alone, the honest conclusion is
  *concentrate + use the vol-target as the deployment lever* (per-sleeve added nothing) — report that plainly,
  do NOT override the gate. If `{positioning, xsfunding}` survives, per-sleeve is the diversified win.

## 6. Scope

**In:** the `fund_config` table + migration + seed row; `application/fund_config.py` (read/write/fallback +
validation); `fxhnt fund-config show|set` CLI; `record_allocation` optional `policy` param + `cockpit_forward`
wiring; the registry tier/promote changes; the provider crypto-sleeve generalization; the falsification-gate
in-cluster run + report; tests; local cockpit verification that `bybit_4edge`-as-reference renders sanely.

**Out (follow-on):** `execute-bybit` per-sleeve book sizing (execution is suspended); a real equity-ADV
capacity model for IBKR; raising `capital_ceiling_live` above $35k / going live (the deliberate human gate);
any new execution path; making `max_strategy_weight` / `max_leverage` / `capital_ceiling` CLI-tunable (only
capital + target_vol + kelly are in scope — the three the user is actively choosing).

## 7. Critical review — where this could go wrong

- **Per-sleeve may not diversify** — §5 is the guard. `unlock` is a known LOO failure; do not force it in. The
  gate reports the truth; accept it.
- **`bybit_4edge` → research is a cockpit change.** The fund's crypto headline book moves from the deploy
  section to research (with its exec/levered twins). Verify locally that the cockpit renders this without a
  broken headline or an empty deploy section before merge (the deploy section then shows the sleeves).
- **Promoting sleeves via `promote_on_pass`, not a hard tier flip.** This respects the forward gate. It means
  the per-sleeve allocation is *gradual* (sleeves join on gate PASS), not instant — a feature, not a bug, but
  state it so "why is xsfunding not allocated yet" is answerable (its gate is WAIT 8/14).
- **20% vol is a real risk increase** over 12%. At risk-parity the low-vol `xsfunding` (if it survives)
  dominates the base weight (capped 0.5) and `K` can run toward `max_leverage 1.5`. Report the actual
  deployed gross + implied book vol in the G2 run so the number is visible, not assumed.
- **`fund_config` fallback must be byte-identical** to today when the table/row is absent — a fresh DB or a
  failed read must not change the allocation. Test this explicitly (fallback == settings default).
- **Partial-override upsert** must not reset unspecified fields to defaults (a classic upsert bug):
  `set --capital X` must leave target_vol/kelly at their current values. Test it.
- **Single-writer / idempotent seed:** the seed uses `ON CONFLICT DO NOTHING`; a re-applied migration must not
  overwrite an operator-set value.

## 8. Decisions locked (from the design dialogue)

- Knobs: **capital + target_vol + kelly_fraction**, one persisted `fund_config` DB row, CLI-set only (never
  hand-edited), read by the nightly with a settings fallback. Initial: **capital $100k, target_vol 0.20, kelly
  0.5**.
- Per-sleeve: **promote `xsfunding` + `unlock` via `promote_on_pass`; demote `bybit_4edge` to a research
  reference track; generalize the provider to honest-size any crypto sleeve.** Marginal gate KEPT.
- A **predetermined falsification gate** (§5) decides whether per-sleeve actually deploys more; the honest
  verdict is accepted either way. Scope stays **paper**; execution wiring is a follow-on.
