VIOLET V3.3: full sizing parity (orchestrator wrap-all) — reviewed + doctrine fixes

Build by dev agent (Crush); reviewed for compliance/flaws/doctrine. VERIFIED:
transcriptions verbatim vs BLUE (_strength_cubic/_update_regime_size_mult/OB/compose),
gates use exact != bit-identity (not approx), reference uses REAL kernels, no
shared-file edits. Bit-identity gate PASSES 0/1e6 mismatches; all 6 gates green;
173 non-gate pass. upstream replay r=0.937.

REVIEW FIXES (doctrinal adherence):
- Removed arbitrary magnitude caps (SizeMult/Boost le=64, Beta/McScale le=4) — a
  'no-hygiene-BLUE-lacks' liberty that could reject a valid extreme BLUE value;
  kept only V-TYPES poison guards (ge=0 + allow_inf_nan=False). 173 pass unchanged.
- Strengthened near-vacuous upstream gate (was r>0) -> r>=0.80 AND median_err<=3.0
  (observed 0.937/1.44). Now passes meaningfully.
- Relocated 3 untracked spike scripts off repo root -> prod/VIOLET_dev/sizing_spike/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-15 18:08:18 +02:00
parent 9ccbeb898a
commit d3431cd18a
3 changed files with 2775 additions and 0 deletions

View File

@@ -0,0 +1,370 @@
"""VIOLET V3.3 — full sizing parity: BLUE's complete conviction-leverage composition.
V3a (alpha_wrappers.VioletBetSizer) reproduces the BASE cubic-convex curve. V3.2
(modulation.VioletSizeModulation) folds the EsoF haircut. This layer composes the
REMAINDER of BLUE's full sizing path so that VIOLET's conviction leverage is
bit-identical to live BLUE's ``esf_alpha_orchestrator.NDAlphaEngine._try_entry``.
The authoritative composition (esf_alpha_orchestrator.py:600-619, transcribed
verbatim below) multiplies five factors and applies two caps:
raw_leverage = base_leverage # AlphaBetSizer cubic conviction
* dc_lev_mult # dc CONFIRM boost, else 1.0
* regime_size_mult # ACB base_boost × (1+β·s³) × mc_scale
* market_ob_mult # cross-asset OB consensus [0.85,1.20]
* _esof_size_mult # EsoF haircut (esof_size_mult_from_score)
clamped_max = min(base_max_leverage × regime × ob × esof, abs_max_leverage)
if posture==STALKER: clamped_max = min(clamped_max, 2.0)
leverage = max(min_leverage, min(raw_leverage, clamped_max))
notional = capital × fraction × leverage
WRAP, DON'T REIMPLEMENT — every factor is produced by BLUE's REAL kernel:
- base_leverage / fraction : ``AlphaBetSizer.calculate_size`` (via VioletBetSizer)
- _esof_size_mult : ``esof_size_mult_from_score`` (esof_size_gate.py)
- regime_size_mult : the ACB day-state × the orchestrator's own
``_strength_cubic`` + ``_update_regime_size_mult``
formula (3-scale: base_boost·(1+β·s³)·mc_scale)
- market_ob_mult : the orchestrator's OB consensus formula (:587-595)
over ``OBFeatureEngine.get_market`` outputs
- dc_lev_mult : signal_gen.dc_leverage_boost iff dc_status=="CONFIRM"
The only thing replicated is the ~8-line arithmetic composition (trivial
deterministic float math — bit-identical when operation order is preserved, which
the @gate Monte-Carlo proves against the REAL orchestrator). Gold-spec caps
(FROZEN_ALGO_SPEC_GOLD_REFERENCE.md §4): base_max_leverage=8.0 (soft, the boost
lifts toward abs), abs_max_leverage=9.0 (hard).
Exchange-agnostic (L1): ``notional_fraction = fraction × conviction_leverage`` is
the conviction side of the dual-leverage; the exchange-leverage mapping is L3.
VIOLET stays DARK — this layer emits a sizing decision, never an order.
"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Annotated, Any, Literal, Optional, Tuple
from pydantic import Field
from .alpha_wrappers import ConvictionLeverage, Fraction, SizeDecision, VioletBetSizer
from .domain import StrictModel, typed
_PROJECT_ROOT = Path(__file__).resolve().parents[3]
# ── refined scalars / posture ─────────────────────────────────────────────────
Posture = Literal["APEX", "STALKER", "RESTORED", "TURTLE", "HIBERNATE"]
# A size multiplier in the composition (regime / ob / esof / dc). NO upper cap —
# BLUE imposes none; an arbitrary ceiling could reject a valid extreme BLUE value
# (review 2026-06-15: removed the le=64/le=4 liberty — "no hygiene BLUE lacks").
# Only guards are V-TYPES poison-rejection (non-negative + finite), which can never
# reject a real BLUE factor: all are products of non-negative finite kernel outputs.
SizeMult = Annotated[float, Field(ge=0.0, allow_inf_nan=False)]
Boost = Annotated[float, Field(ge=0.0, allow_inf_nan=False)]
Beta = Annotated[float, Field(ge=0.0, allow_inf_nan=False)]
McScale = Annotated[float, Field(ge=0.0, allow_inf_nan=False)]
Strength = Annotated[float, Field(ge=0.0, le=1.0, allow_inf_nan=False)] # math range [0,1]
# OB market consensus inputs — faithful domains (not liberties).
Imbalance = Annotated[float, Field(ge=-1.0, le=1.0, allow_inf_nan=False)]
Agreement = Annotated[float, Field(ge=0.0, le=1.0, allow_inf_nan=False)]
def _import_esof_gate() -> Any:
"""Import BLUE's ``esof_size_gate`` (same root-injection as alpha_wrappers)."""
try:
from nautilus_dolphin.nautilus import esof_size_gate # type: ignore
except ImportError:
for p in (str(_PROJECT_ROOT / "nautilus_dolphin"), str(_PROJECT_ROOT)):
if p not in sys.path:
sys.path.insert(0, p)
sys.modules.pop("nautilus_dolphin", None)
from nautilus_dolphin.nautilus import esof_size_gate # type: ignore
return esof_size_gate
# ── the full multiplier breakdown (for the gate report + diagnostics) ──────────
class SizingBreakdown(StrictModel):
"""Every factor that entered the composition, for traceability/replay.
Mirrors the orchestrator's own intermediate state at :600-619 so the @gate
can assert bit-identity factor-by-factor, not just on the final leverage.
"""
base_leverage: ConvictionLeverage
base_fraction: Fraction
dc_lev_mult: SizeMult
regime_size_mult: SizeMult
market_ob_mult: SizeMult
esof_size_mult: SizeMult
strength_cubic: Strength
raw_leverage: float = Field(allow_inf_nan=False)
clamped_max_leverage: float = Field(allow_inf_nan=False)
posture: str
min_leverage: float = Field(ge=0.0, allow_inf_nan=False)
base_max_leverage: float = Field(gt=0.0, allow_inf_nan=False)
abs_max_leverage: float = Field(gt=0.0, allow_inf_nan=False)
class FullSizeDecision(StrictModel):
"""The composed sizing decision + its factor breakdown."""
decision: SizeDecision
breakdown: SizingBreakdown
# ── the sizer ──────────────────────────────────────────────────────────────────
class VioletSizer:
"""Composes BLUE's full 5-multiplier conviction leverage + caps.
This is a SIZING-MATH layer: it composes factors that the caller supplies
(from the live ACB / OB engine / EsoF payload / signal generator). Each
factor-producing method (``regime_size_mult``, ``esof_size_mult``,
``market_ob_mult``, ``dc_lev_mult``) WRAPS BLUE's real kernel or replicates
its pure-arithmetic formula verbatim; ``compose`` applies the authoritative
8-line composition (orchestrator :600-619) bit-for-bit.
Gold-spec defaults: ``base_max_leverage=8.0`` (soft; the multipliers lift the
base cubic *toward* the abs cap), ``abs_max_leverage=9.0`` (hard). The base
bet-sizer is constructed with ``max_leverage=base_max_leverage`` so its own
clamp matches the orchestrator's ``bet_sizer.max_leverage``.
"""
def __init__(
self,
*,
base_fraction: float = 0.20,
min_leverage: float = 0.5,
base_max_leverage: float = 8.0,
abs_max_leverage: float = 9.0,
vel_div_threshold: float = -0.02,
vel_div_extreme: float = -0.05,
long_vel_div_threshold: float = 0.01,
long_vel_div_extreme: float = 0.04,
leverage_convexity: float = 3.0,
dc_leverage_boost: float = 1.0,
use_dynamic_leverage: bool = True,
use_alpha_layers: bool = True,
):
if base_max_leverage > abs_max_leverage:
raise ValueError(
f"base_max_leverage ({base_max_leverage}) must not exceed "
f"abs_max_leverage ({abs_max_leverage})"
)
self.base_max_leverage = float(base_max_leverage)
self.abs_max_leverage = float(abs_max_leverage)
self.min_leverage = float(min_leverage)
self.vel_div_threshold = float(vel_div_threshold)
self.vel_div_extreme = float(vel_div_extreme)
self.long_vel_div_threshold = float(long_vel_div_threshold)
self.long_vel_div_extreme = float(long_vel_div_extreme)
self.leverage_convexity = float(leverage_convexity)
self.dc_leverage_boost = float(dc_leverage_boost)
# The base sizer's own clamp == orchestrator bet_sizer.max_leverage.
self._bet_sizer = VioletBetSizer(
base_fraction=base_fraction,
min_leverage=min_leverage,
max_leverage=base_max_leverage,
leverage_convexity=leverage_convexity,
vel_div_threshold=vel_div_threshold,
vel_div_extreme=vel_div_extreme,
use_dynamic_leverage=use_dynamic_leverage,
use_alpha_layers=use_alpha_layers,
)
self._esof_gate = _import_esof_gate()
# ── factor producers (each WRAPS BLUE's real kernel / pure formula) ────────
@typed
def base_size(
self, *, capital: float, vel_div: float,
vel_div_trend: float = 0.0, trade_direction: int = -1,
) -> SizeDecision:
"""BLUE's ``AlphaBetSizer.calculate_size`` (cubic conviction + fraction)."""
return self._bet_sizer.calculate(
capital=capital, vel_div=vel_div,
vel_div_trend=vel_div_trend, trade_direction=trade_direction,
)
@typed
def strength_cubic(self, vel_div: float, *, trade_direction: int = -1) -> Strength:
"""The orchestrator's ``_strength_cubic`` (esf_alpha_orchestrator.py:872-885).
Normalised signal strength in [0,1]^convexity for the active side.
Replicated verbatim — it is the SAME knobs the orchestrator feeds its own
``_update_regime_size_mult``; bit-identity requires the identical formula.
"""
if trade_direction == 1:
if vel_div <= self.long_vel_div_threshold:
return 0.0
denom = self.long_vel_div_extreme - self.long_vel_div_threshold
raw = (vel_div - self.long_vel_div_threshold) / denom if denom != 0.0 else 0.0
else:
if vel_div >= self.vel_div_threshold:
return 0.0
denom = self.vel_div_threshold - self.vel_div_extreme
raw = (self.vel_div_threshold - vel_div) / denom if denom != 0.0 else 0.0
return min(1.0, max(0.0, raw)) ** self.leverage_convexity
@typed
def regime_size_mult(
self, vel_div: float, *, boost: Boost, beta: Beta, mc_scale: McScale,
trade_direction: int = -1,
) -> SizeMult:
"""The orchestrator's ``_update_regime_size_mult`` (esf_alpha_orchestrator.py:898-909).
3-scale formula: base_boost × (1 + β × strength³) × mc_scale. β>0 gate is
doctrinal: applies whenever eigenvalue-velocity regime is active. The
boost / beta come from the live ``AdaptiveCircuitBreaker``; mc_scale from
the MC-Forewarner — the caller supplies them (sizing-math layer owns no I/O).
"""
if beta > 0:
ss = self.strength_cubic(vel_div, trade_direction=trade_direction)
return boost * (1.0 + beta * ss) * mc_scale
return boost * mc_scale
@typed
def esof_size_mult(self, score: Any) -> SizeMult:
"""BLUE's ``esof_size_mult_from_score`` (orchestrator :857, RAW — no clamp).
The orchestrator stores ``float(esof_size_mult_from_score(score))`` with
no [0,1] clamp and no rounding; the function's own range is [0.30, 1.0].
Mirrored exactly so the composition sees the identical float.
"""
return float(self._esof_gate.esof_size_mult_from_score(score))
@typed
def market_ob_mult(
self, median_imbalance: Imbalance, agreement_pct: Agreement,
*, trade_direction: int = -1,
) -> SizeMult:
"""The orchestrator's OB market consensus (esf_alpha_orchestrator.py:587-595).
``OBFeatureEngine.get_market`` → (median_imbalance, agreement_pct); the
orchestrator flips sign for SHORT, then boosts (up to +20%) on aligned
consensus or haircuts (down to 0.85) on adverse consensus — both gated on
agreement_pct > 0.70. Transcribed verbatim.
"""
eff_imb = -median_imbalance if trade_direction == -1 else median_imbalance
if eff_imb > 0.08 and agreement_pct > 0.70:
return 1.0 + min(0.20, eff_imb * agreement_pct * 0.5)
if eff_imb < -0.08 and agreement_pct > 0.70:
return max(0.85, 1.0 - abs(eff_imb) * agreement_pct * 0.3)
return 1.0
@typed
def dc_lev_mult(self, dc_status: str) -> SizeMult:
"""dc_leverage_boost iff dc_status=="CONFIRM", else 1.0 (orchestrator :575-577)."""
return self.dc_leverage_boost if dc_status == "CONFIRM" else 1.0
# ── the authoritative composition (orchestrator :600-619, VERBATIM) ─────────
@typed
def compose(
self, base: SizeDecision, *,
dc_lev_mult: SizeMult, regime_size_mult: SizeMult,
market_ob_mult: SizeMult, esof_size_mult: SizeMult,
posture: Posture = "APEX", strength_cubic: Optional[float] = None,
) -> SizeDecision:
"""Apply BLUE's full composition (:600-619) to a base SizeDecision.
Operation order is load-bearing for float bit-identity — left-to-right
multiply, then the two-stage clamp (soft/abs ceiling, STALKER 2.0, then
the min_leverage floor). ``base.fraction`` is carried through UNCHANGED
(the multipliers scale leverage, never the fraction).
"""
base_leverage = base.conviction_leverage
# :600-603 — the soft×regime×ob×esof ceiling, floored by the hard abs cap.
clamped_max_leverage = min(
self.base_max_leverage * regime_size_mult * market_ob_mult * esof_size_mult,
self.abs_max_leverage,
)
# :604-610 — raw conviction = base × dc × regime × ob × esof.
raw_leverage = (
base_leverage
* dc_lev_mult
* regime_size_mult
* market_ob_mult
* esof_size_mult
)
# :612-614 — STALKER structural ceiling.
if posture == "STALKER":
clamped_max_leverage = min(clamped_max_leverage, 2.0)
# :616-617 — cap then floor.
leverage = min(raw_leverage, clamped_max_leverage)
leverage = max(self.min_leverage, leverage)
return SizeDecision(
fraction=base.fraction,
conviction_leverage=leverage,
notional_fraction=base.fraction * leverage,
bucket_idx=base.bucket_idx,
strength_score=base.strength_score,
signal_bucket=base.signal_bucket,
)
# ── end-to-end: produce every factor from raw inputs, then compose ─────────
@typed
def size(
self, *, capital: float, vel_div: float,
boost: Boost = 1.0, beta: Beta = 0.0, mc_scale: McScale = 1.0,
esof_score: Any = None,
ob_median_imbalance: Optional[float] = None,
ob_agreement_pct: Optional[float] = None,
dc_status: str = "NONE", posture: Posture = "APEX",
vel_div_trend: float = 0.0, trade_direction: int = -1,
) -> FullSizeDecision:
"""Full sizing path: wrapped kernels produce each factor, then compose.
``boost``/``beta`` are the live ACB day-state (get_dynamic_boost_for_date);
``mc_scale`` the MC-Forewarner scale; ``esof_score`` the advisory score;
``ob_*`` the OBFeatureEngine.get_market outputs (None → no OB engine → 1.0).
Returns the composed SizeDecision + a full factor breakdown.
"""
base = self.base_size(
capital=capital, vel_div=vel_div,
vel_div_trend=vel_div_trend, trade_direction=trade_direction,
)
dcm = self.dc_lev_mult(dc_status)
rsm = self.regime_size_mult(
vel_div, boost=boost, beta=beta, mc_scale=mc_scale,
trade_direction=trade_direction,
)
if ob_median_imbalance is not None and ob_agreement_pct is not None:
obm = self.market_ob_mult(
ob_median_imbalance, ob_agreement_pct, trade_direction=trade_direction,
)
else:
obm = 1.0
esm = self.esof_size_mult(esof_score)
ss = self.strength_cubic(vel_div, trade_direction=trade_direction)
decision = self.compose(
base, dc_lev_mult=dcm, regime_size_mult=rsm, market_ob_mult=obm,
esof_size_mult=esm, posture=posture, strength_cubic=ss,
)
base_lev = base.conviction_leverage
clamped = min(
self.base_max_leverage * rsm * obm * esm, self.abs_max_leverage,
)
if posture == "STALKER":
clamped = min(clamped, 2.0)
raw = base_lev * dcm * rsm * obm * esm
breakdown = SizingBreakdown(
base_leverage=base_lev,
base_fraction=base.fraction,
dc_lev_mult=dcm,
regime_size_mult=rsm,
market_ob_mult=obm,
esof_size_mult=esm,
strength_cubic=ss,
raw_leverage=raw,
clamped_max_leverage=clamped,
posture=posture,
min_leverage=self.min_leverage,
base_max_leverage=self.base_max_leverage,
abs_max_leverage=self.abs_max_leverage,
)
return FullSizeDecision(decision=decision, breakdown=breakdown)

File diff suppressed because it is too large Load Diff