Per-wallet bet sizing already exists in pmv2 as pmv2_wallets.size_scale (a per-wallet global multiplier), so scaling crypto’s bet needs no crypto code and no new schema. CORRECTION (2026-07-06): an earlier version of this note claimed the executor “reads size_scale live” — that is WRONG. size_scale is captured once at position_manager boot and held as a static value for the process lifetime; a DB write does nothing until position_manager is restarted. Verified from code + a live DB experiment on 2026-07-06.

CORRECTION — size_scale is NOT hot-read (do not rely on a live DB write)

pmv2_wallets.size_scale is loaded ONCE at position_manager boot and passed as a static Decimal into each wallet’s consumer task. That task runs for the whole process lifetime with the boot-time scale. Writing pmv2_wallets.size_scale in the DB has NO effect on the running executor until it is restarted.

Where the earlier “reads live” claim came from: size_scale sits in the same pmv2_wallets table as mode, and the source mode toggle is hot-read per directive — so the note wrongly assumed the adjacent column behaves the same. The executor code paths differ: mode is re-read per directive; size_scale is snapshotted at boot.

For Agents — binding facts (corrected)

  1. pmv2_wallets.size_scale NUMERIC NOT NULL DEFAULT 1.0 — a per-wallet GLOBAL size multiplier. It is loaded once at position_manager boot; a DB change requires a position_manager RESTART to take effect (NOT hot-read).
  2. There is NO per-source scale. pmv2_source_wallets (source_id, wallet_id) only maps sources → wallets; it carries no scale column. So sizing is per wallet, not per source/lane.
  3. Scaling crypto’s bet = writing one DB column + a position_manager restart. No crypto code, no migration — but the DB write alone is a silent no-op on the running process. A command surface was requested as babylon pmv2 #1065 (and it MUST force a registry reload — see below).
  4. CONTRAST: source mode (off/dry_run/live, on pmv2_autotrade_sources) IS hot-read per directive — which is why toggling a lane’s mode takes effect live without a restart. Wallet size_scale is the exception.
  5. GOTCHA: a size_scale bump must also be matched by that wallet’s daily_spend_cap_usd or the cap silently clips the larger bets (the cap is a reject-gate, not a resizer).

Where it is boot-loaded (code)

In position_manager/crates/position_manager/src/bin/position-manager.rs:

  • main() builds a scale_by_wallet HashMap (~line 204) from the pmv2_wallets rows.
  • It then spawns one consumer task per wallet, passing that wallet’s scale as a static Decimal: consumer::run_wallet(pm, clob, client, wallet_id, scale) (~line 322).
  • That task runs for the process lifetime with the boot-time scale — it never re-reads pmv2_wallets.size_scale.

So the value is frozen at process start. A DB update to pmv2_wallets.size_scale does nothing until position_manager is restarted.

Evidence — the 2026-07-06 live experiment

On 2026-07-06, @deploy set pmv2_wallets.size_scale from 1.02.0 for both wallets (DB confirmed at 2.0). But live fills were unchanged:

  • pmv2_autotrade_orders continued to show filled_size = 10 shares / size_usd ≈ $8.
  • If the 2.0 had applied, fills would be ~20 shares / ~$16.

The running position_manager kept the boot-time 1.0. This is the direct confirmation that size_scale is boot-loaded, not hot-read — a restart is required to apply the new value.

The full sizing formula (two compounding levers)

For crypto’s Limit orders:

size_usd    = limit_price × size_shares × size_scale
size_shares = ceil(min_size) × EMIT_SIZE_MULTIPLIER

With EMIT_SIZE_MULTIPLIER = 2 (our crypto emit env var) → size_shares = 10 shares currently. There are TWO size levers that compound, each with a different deploy requirement:

LeverWhere it livesTo change it you must…
EMIT_SIZE_MULTIPLIERcrypto emit env var (producer-side, per-trade size_shares)redeploy the emit container
pmv2_wallets.size_scalepmv2 DB (PM-side, per-wallet global)restart position_manager (boot-loaded — a DB write alone does nothing)

limit_price is break-even-priced by emit (see crypto-shortterm-emit-guardrails / project CLAUDE.md observe::fire_limit), not a size lever you tune. The two size levers are EMIT_SIZE_MULTIPLIER and size_scale; both take effect only after their respective deploy step.

The schema (already exists)

position_manager/supabase/migrations/20260701000000_pmv2_wallets.sql gives each wallet, on pmv2_wallets:

ColumnMeaning
size_scale NUMERIC NOT NULL DEFAULT 1.0per-wallet global bet-size multiplier — boot-loaded; a DB change needs a position_manager restart
mode (off / dry_run / live)per-wallet arm state (command-controlled, with a ledger)
daily_spend_cap_usdper-wallet daily spend reject-gate
total_exposure_cap_usdper-wallet total-exposure reject-gate

pmv2_source_wallets (source_id, wallet_id) maps sources → wallets and has no scale column — confirming size_scale is the only sizing knob and it is per wallet, global.

No crypto code, no new schema (but not “just a command”)

“Per-wallet bet sizing for crypto” is a solved capability on the pmv2 side — the lever exists and crypto writes nothing. What’s missing is an operator-facing command to write it AND make the running executor pick it up: because size_scale is boot-loaded, a bare DB write is a silent no-op until restart.

How this composes with the producer-side lever

This is a PM-side, per-wallet, global multiplier — distinct from the producer-side, per-trade lever size_shares documented in crypto-shortterm-phase1-bet-sizing (which @positionmanager honors directly, pricing.rs). size_scale (migration dated 2026-07-01) scales on top of whatever size_shares the directive carries. Two multiplicative layers: producer sets per-trade size_shares (via EMIT_SIZE_MULTIPLIER, emit redeploy); PM multiplies by the wallet’s size_scale (DB + position_manager restart).

The requested command (babylon pmv2 #1065)

Requested from @positionmanager as the sibling of the existing mode command:

  • Writes pmv2_wallets.size_scale.
  • Ledgered — same pattern as the mode command’s mode_control_ledger (auditable history of who set what).
  • Clamped to [0.5, 3].
  • MUST force a reload — this is the binding requirement. Writing pmv2_wallets.size_scale alone does nothing to the running executor (it captured the scale at boot). The command must EITHER (a) trigger a position_manager wallet-registry reload / restart, OR (b) be shipped alongside an executor change that re-reads size_scale per order. Without one of these, the command writes the DB and the running process ignores it — the exact failure observed on 2026-07-06.

Gotcha: bump size_scale AND the daily cap together

daily_spend_cap_usd is a reject-GATE, not a resizer — it rejects orders that would breach the cap, it does not shrink them to fit (the exact mechanic established in crypto-shortterm-phase1-bet-sizing for the source-level cap). So a bigger size_scale burns a fixed daily cap faster → fewer fills, silently. Any size_scale bump must be paired with a matching daily_spend_cap_usd bump on that wallet, or the intended larger bets get clipped and you just get fewer trades at the old effective spend.

Use case: de-risk the ~2026-07-09 week-read 2× decision

Around 2026-07-09 there is a week-read decision on whether to double the bet. The per-wallet global size_scale turns that into a clean control experiment instead of an all-or-nothing flip:

  • Set wallet1.size_scale = 2.0 and keep wallet2.size_scale = 1.0 as the control.
  • Both wallets are crypto-dedicated and trade the same windows/sides (wallet1 = 0xAdAc17C0…aEb2 “bandiboss”, wallet2 = 0x7D39B0e5…8ee6 “Balu” — see crypto-shortterm-pnl-onchain-cash-truth-2026-07-06). Because the scale is per-wallet global, wallet1.size_scale=2.0 = 2× across ALL crypto lanes for wallet1, with wallet2 as a matched 1× baseline running identical signals.
  • Remember the two gotchas:
    1. The DB write requires a position_manager restart to take effect (boot-loaded — the 2026-07-06 experiment proved a bare write is ignored by the running process).
    2. Bump wallet1.daily_spend_cap_usd to match the 2× or the doubled bets get cap-clipped and the A/B is confounded.