Files
siloqy/prod/docs/VIOLET_BUILD_SPEC__SIZING_PARITY.md
Codex 9ccbeb898a VIOLET build spec: add repo cwd + must-run-on-host constraint
Repo cwd /mnt/dolphinng5_predict (git root), no remote (local-only) -> agent must
run on this host in this dir; needs host-local eigenvalues data, live ClickHouse,
and BLUE runtime for bit-identity. Adds CH creds + python path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:12:05 +02:00

7.5 KiB
Raw Blame History

VIOLET Build Spec — Full Sizing Parity (orchestrator wrap-all → bit-identity)

Status: READY TO BUILD. Self-contained brief; no prior session context assumed.

Repo cwd: /mnt/dolphinng5_predict (git root). Branch exp/pink-ditav2-sprint0-20260530. No git remote — local-only repo. ⟹ the build agent MUST run ON THIS HOST in this directory; it cannot clone elsewhere, and the build needs host-local resources regardless: the eigenvalues data on disk (/mnt/dolphin_training/data/eigenvalues or sibling), the live ClickHouse (http://localhost:8123, user dolphin / key dolphin_ch_2026), and BLUE's actual code/runtime for the bit-identity comparison. Python: /home/dolphin/siloqy_env/bin/python3.

Background/derivation: VIOLET_V3_FINDINGS.md §8b/§8c. Doctrine: memory violet_v3_alpha_doctrine (if loaded) — key rules restated below.

1. Objective

Make VIOLET's sizing reproduce live BLUE's conviction-leverage bit-for-bit. VIOLET already reproduces the base cubic curve (V3a) and the EsoF haircut (V3.2). What's missing is the rest of BLUE's full sizing composition (3 more multipliers + cap logic), which lives in esf_alpha_orchestrator, not in the base bet-sizer. Wrap those, compose exactly, and prove identity with a Monte-Carlo gate.

2. Non-negotiable constraints

  • WRAP, DON'T REIMPLEMENT. Call BLUE's actual kernels; do not re-derive their math. Bit-identity is only achievable by running the real code. (Reimplementation will fail the gate on float ordering.)
  • ZERO edits to shared files: prod/nautilus_event_trader.py, prod/clean_arch/dita_v2/*, prod/clean_arch/dita/decision.py, nautilus_dolphin/**, blue_parity.py. Mechanical check per commit: git diff --name-only must not contain them.
  • VIOLET stays DARK — no execution, no orders. This is a sizing-math layer only.
  • V-TYPES (prod/clean_arch/violet/domain.py): refined types at boundaries, @typed (beartype) on public methods, StrictModel for value objects, reject-at-source.
  • Follow BLUE in all regards — no filters/hygiene BLUE lacks.

3. The exact target composition (authoritative)

Source: nautilus_dolphin/nautilus_dolphin/nautilus/esf_alpha_orchestrator.py ~lines 597-619. Reproduce in EXACT operation order (float order matters for bit-identity):

raw_leverage = size_result["leverage"]   # base cubic (AlphaBetSizer)
             * dc_lev_mult               # signal_gen.dc_leverage_boost if signal.dc_status=="CONFIRM" else 1.0
             * regime_size_mult          # ACB: _day_base_boost * (1 + _day_beta * strength^3) * _day_mc_scale
             * market_ob_mult            # OB cross-asset consensus (1.0 default; 0.85..1.20)
             * _esof_size_mult           # EsoF haircut [0,1]
clamped_max  = min(base_max_leverage * regime_size_mult * market_ob_mult * _esof_size_mult, abs_max_leverage)
if _day_posture == 'STALKER': clamped_max = min(clamped_max, 2.0)
leverage     = min(raw_leverage, clamped_max)
leverage     = max(bet_sizer.min_leverage, leverage)
notional     = capital * size_result["fraction"] * leverage

Gold-spec caps (prod/docs/FROZEN_ALGO_SPEC_GOLD_REFERENCE.md): base_max_leverage=8.0 (soft), abs_max_leverage=9.0 (hard). NOTE V3a currently constructs the base sizer with max_leverage=9.0change to 8.0 (the boost lifts toward 9).

4. Wrap surfaces (what to wrap, where)

Multiplier Wrap target API
base size_result nautilus_dolphin/.../alpha_bet_sizer.py AlphaBetSizer.calculate_size already wrapped: prod/clean_arch/violet/alpha_wrappers.py VioletBetSizer (fix max_leverage=8.0)
_esof_size_mult nautilus_dolphin/.../esof_size_gate.py esof_size_mult_from_score already wrapped: prod/clean_arch/violet/modulation.py VioletSizeModulation
regime_size_mult nautilus_dolphin/.../adaptive_circuit_breaker.py AdaptiveCircuitBreaker preload_w750([dates]), get_dynamic_boost_for_date(date)/get_dynamic_boost_from_hz(...){boost, beta}; per-bar regime_size_mult = base_boost*(1+beta*strength^3)*mc_scale (orchestrator :901-909). Needs eigenvalues data (auto-resolves to /mnt/dolphin_training/data/eigenvalues etc.)
dc_lev_mult esf_alpha_orchestrator signal_gen (signal.dc_status, signal_gen.dc_leverage_boost) wrap the signal generator; dc_lev_mult = dc_leverage_boost if dc_status=="CONFIRM" else 1.0
market_ob_mult nautilus_dolphin/.../ob_features.py OBFeatureEngine get_market(bar_idx, symbols) → imbalance/agreement; formula at orchestrator :587-595
_day_posture (STALKER) orchestrator posture state 2.0 cap when STALKER

Preferred approach (most faithful): instantiate and drive the REAL esf_alpha_orchestrator sizing path so the composition runs BLUE's own code. If full orchestrator instantiation proves too heavy, the fallback is to wrap each component above and replicate ONLY the ~8-line composition block verbatim (it is trivial deterministic arithmetic — bit-identical if op-order is preserved). Decide after a spike on orchestrator instantiation cost.

5. Validation gate (BINDING — operator-specified)

  1. Monte-Carlo the ENTIRE JOINT input universe of both surfaces together: vel_div × ACB signals(funding/dvol/fng/taker) × w750_vel/β × esof_score × mc_scale × ob imbalance/agreement × posture × capital. Hammer interactions (cap@9, EsoF-on-boosted, STALKER). N ≥ 1e6 samples.
  2. Match to BIT IDENTITY vs BLUE's actual-code output (float-for-float, ==, not approx). A statistical match HIDES composition bugs; bit-identity won't. Any mismatch = wrapper bug (op order / rounding / cap) → fix → re-run.
  3. THEN upstream — replay recorded dolphin.trade_events (and/or live scans) through the wrapped chain; compare to recorded leverage. (Caveat: recorded boost_at_entry/ beta_at_entry are mostly placeholder 1.0 — do NOT validate against those fields; validate against leverage itself, and use the live ACB to produce boosts.)

6. Reusable existing pieces

  • prod/clean_arch/violet/alpha_wrappers.pyVioletBetSizer, SizeDecision (V-TYPES).
  • prod/clean_arch/violet/modulation.pyVioletSizeModulation (EsoF fold, the wrap pattern).
  • prod/clean_arch/violet/test_violet_modulation.py / test_violet_alpha_wrappers.py — test patterns (hypothesis + drift-guards) to mirror.
  • Import-root pattern for nautilus_dolphin.nautilus.*: see _import_esof_gate() in modulation.py / _import_blue_alpha() in alpha_wrappers.py.

7. Deliverables & acceptance

  • New prod/clean_arch/violet/sizing.py (or extend modulation.py): a VioletSizer that composes the 5 multipliers + caps, returning a V-TYPES SizeDecision with the full conviction leverage.
  • test_violet_sizing.py: unit + hypothesis + the MC bit-identity gate (@pytest.mark.gate)
    • the upstream replay check. Gate report → prod/VIOLET_dev/reports/.
  • ACCEPT when: bit-identity gate passes at N≥1e6; upstream replay matches recorded leverage within tolerance attributable only to live-ACB vs recorded; full violet suite green; shared-files-clean; VIOLET still DARK.

8. Watch-outs (learned)

  • boost_at_entry/beta_at_entry in trade_events = placeholder 1.0 (don't trust them).
  • beta recorded as {0,1} in some places vs config {0.2,0.8} — get beta from the live ACB, not recorded fields.
  • ACB needs eigenvalues data on disk; verify the path resolves on the prod host before the upstream step.
  • min_leverage floor and the STALKER 2.0 cap are easy to forget — both are in the gate.