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:
@@ -31,6 +31,17 @@ from .cadence import Action, CadenceControlPlane
|
|||||||
from .domain import StrictModel, Symbol, typed
|
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):
|
class ShadowDecision(StrictModel):
|
||||||
"""One muted decision — what BLUE *would* do this scan. Never executed."""
|
"""One muted decision — what BLUE *would* do this scan. Never executed."""
|
||||||
|
|
||||||
@@ -107,6 +118,8 @@ class VioletDecisionEngine:
|
|||||||
if px <= 0:
|
if px <= 0:
|
||||||
continue
|
continue
|
||||||
sym = str(asset).upper()
|
sym = str(asset).upper()
|
||||||
|
if sym in STABLECOIN_SYMBOLS: # BLUE :3906 — never a trade asset
|
||||||
|
continue
|
||||||
hist = self._history.get(sym)
|
hist = self._history.get(sym)
|
||||||
if hist is None:
|
if hist is None:
|
||||||
hist = deque(maxlen=maxlen)
|
hist = deque(maxlen=maxlen)
|
||||||
|
|||||||
@@ -8,8 +8,13 @@ sys.path.insert(0, "/mnt/dolphinng5_predict")
|
|||||||
|
|
||||||
import pytest
|
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.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
|
LOOKBACK = 5
|
||||||
|
|
||||||
@@ -101,6 +106,32 @@ def test_engine_holds_no_venue_or_kernel():
|
|||||||
assert not hasattr(e, attr)
|
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():
|
def test_determinism_same_inputs_same_decision():
|
||||||
e1, e2 = _engine(), _engine()
|
e1, e2 = _engine(), _engine()
|
||||||
_warm(e1); _warm(e2)
|
_warm(e1); _warm(e2)
|
||||||
|
|||||||
Reference in New Issue
Block a user