VIOLET V3.1: BLUE stablecoin exclusion (parity fix)

Soak surfaced USDCUSDT being shorted. BLUE has a hardcoded exclusion gate around
its (muted-IRP) picking: _STABLECOIN_SYMBOLS removed from prices_dict pre-select
(nautilus_event_trader.py:24/3906). Replicate exactly: VioletDecisionEngine skips
the same 10 symbols in observe() so IRP never sees them. Following BLUE in all
regards (picking unchanged; this is BLUE's separate gate). Set-equality drift
guard vs BLUE source + never-selected test. 11 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-14 21:51:09 +02:00
parent 02ecc55c16
commit a168d0bee5
2 changed files with 45 additions and 1 deletions

View File

@@ -31,6 +31,17 @@ from .cadence import Action, CadenceControlPlane
from .domain import StrictModel, Symbol, typed
# Stablecoins / pegged assets that must NEVER be selected as a trade asset.
# MUST equal nautilus_event_trader.py:_STABLECOIN_SYMBOLS (BLUE removes these from
# prices_dict before selection, :3906) — asserted by test_violet_decision_engine.
# Following BLUE in all regards: picking stays muted-IRP as BLUE, this is BLUE's
# separate exclusion gate around it.
STABLECOIN_SYMBOLS = frozenset({
"USDCUSDT", "BUSDUSDT", "FDUSDUSDT", "USDTUSDT", "TUSDUSDT",
"DAIUSDT", "FRAXUSDT", "USDDUSDT", "USTCUSDT", "EURUSDT",
})
class ShadowDecision(StrictModel):
"""One muted decision — what BLUE *would* do this scan. Never executed."""
@@ -107,6 +118,8 @@ class VioletDecisionEngine:
if px <= 0:
continue
sym = str(asset).upper()
if sym in STABLECOIN_SYMBOLS: # BLUE :3906 — never a trade asset
continue
hist = self._history.get(sym)
if hist is None:
hist = deque(maxlen=maxlen)

View File

@@ -8,8 +8,13 @@ sys.path.insert(0, "/mnt/dolphinng5_predict")
import pytest
import re
from pathlib import Path
from prod.clean_arch.violet.cadence import Action, CadenceControlPlane, INSTA_Q_NS, SCAN_Q_NS
from prod.clean_arch.violet.decision_engine import ShadowDecision, VioletDecisionEngine
from prod.clean_arch.violet.decision_engine import (
STABLECOIN_SYMBOLS, ShadowDecision, VioletDecisionEngine,
)
LOOKBACK = 5
@@ -101,6 +106,32 @@ def test_engine_holds_no_venue_or_kernel():
assert not hasattr(e, attr)
def test_stablecoin_set_matches_blue_exactly():
# Drift guard: VIOLET's exclusion set MUST equal BLUE's _STABLECOIN_SYMBOLS
# (nautilus_event_trader.py), parsed from source (no heavy import).
src = Path("/mnt/dolphinng5_predict/prod/nautilus_event_trader.py").read_text()
m = re.search(r"_STABLECOIN_SYMBOLS\s*=\s*frozenset\(\{(.*?)\}\)", src, re.DOTALL)
assert m is not None
blue = set(re.findall(r"'([A-Z0-9]+)'", m.group(1)))
assert STABLECOIN_SYMBOLS == blue
def test_stablecoin_never_selected():
# Even with a strong short signal, a stablecoin must never be picked (BLUE :3906).
e = _engine()
assets = ["USDCUSDT", "BBBUSDT", "CCCUSDT"]
for s in range(8):
# USDC trends down hardest (would rank top by IRP if not excluded)
prices = [100.0 * (1 - 0.01 * s), 100.0 * (1 - 0.001 * s), 100.0 * (1 - 0.0005 * s)]
e.observe({"assets": assets, "asset_prices": prices}, scan_number=s + 1)
assert "USDCUSDT" not in e._history # never accumulated
for k in range(3):
d = e.decide(now_ns=10**12 + k * SCAN_Q_NS, scan_number=20 + k,
capital=69_000.0, vel_div=-0.20)
if d is not None:
assert d.asset != "USDCUSDT"
def test_determinism_same_inputs_same_decision():
e1, e2 = _engine(), _engine()
_warm(e1); _warm(e2)