VIOLET V1c: observe-only launcher + hard guard + divergence monitor + service (DARK)

launch_dolphin_violet.py: own namespaces hard-set (CH dolphin_violet, HZ
DOLPHIN_STATE_VIOLET/PNL, Zinc prefix violet, DOLPHIN-VIOLET-001); own
credentials (BINGX_VIOLET_API_KEY/SECRET) — DARK idle with periodic WARNING
until provisioned; CH preflight SELECT-probes the required tables and NEVER
creates (DDL-before-code); kernel snapshot path repointed away from PINK's
fixed /tmp/.pink_kernel_state.json; mainnet hard-disabled; observe loop
never calls runtime.step(). ObserveOnlyVenue: submit/cancel raise
ObserveOnlyViolation with full attribute delegation — the kernel's
venue-submit-failure rollback converts a refusal into a synthetic REJECT
(slot back to IDLE), proven against the real kernel. FeedDivergenceMonitor:
per-asset scan-vs-venue divergence rows (bookTicker WS via
prod/bingx/market_stream, REST fallback) with stale-mid suppression and
plane seq propagation — the FET 0.2176-vs-0.1878 detector; runs even DARK
(public data). Supervisord [program:dolphin_violet] autostart=false, no
keys in conf by design. Violet package: 42 tests green + V0 gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-12 16:09:04 +02:00
parent d639a69307
commit 970c33cb8e
7 changed files with 984 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
"""V1: ObserveOnlyVenue — refusal + delegation contract."""
from __future__ import annotations
import asyncio
import sys
sys.path.insert(0, "/mnt/dolphinng5_predict")
sys.path.insert(0, "/mnt/dolphinng5_predict/nautilus_dolphin")
import pytest
from prod.clean_arch.violet.observe_guard import ObserveOnlyVenue, ObserveOnlyViolation
class _Inner:
def __init__(self):
self.connected = False
self.calls = []
def submit(self, intent):
self.calls.append(("submit", intent))
return ["should-never-happen"]
async def submit_async(self, intent):
self.calls.append(("submit_async", intent))
return ["should-never-happen"]
def cancel(self, order, reason=""):
self.calls.append(("cancel", order))
return []
async def cancel_async(self, order, reason=""):
self.calls.append(("cancel_async", order))
return []
async def connect(self):
self.connected = True
async def subscribe(self):
if False: # pragma: no cover — generator shape
yield None
@pytest.mark.asyncio
async def test_submit_and_cancel_raise_and_never_reach_inner():
inner = _Inner()
guard = ObserveOnlyVenue(inner)
with pytest.raises(ObserveOnlyViolation):
guard.submit({"x": 1})
with pytest.raises(ObserveOnlyViolation):
guard.cancel(object())
with pytest.raises(ObserveOnlyViolation):
await guard.submit_async({})
with pytest.raises(ObserveOnlyViolation):
await guard.cancel_async(object())
assert inner.calls == []
@pytest.mark.asyncio
async def test_attribute_get_delegates():
inner = _Inner()
guard = ObserveOnlyVenue(inner)
assert guard.connected is False
await guard.connect()
assert inner.connected is True and guard.connected is True
def test_attribute_set_delegates_to_inner():
"""The runtime does `venue._kernel_ref = kernel` — assignment must land
on the wrapped adapter, not the guard."""
inner = _Inner()
guard = ObserveOnlyVenue(inner)
sentinel = object()
guard._kernel_ref = sentinel
assert getattr(inner, "_kernel_ref") is sentinel
assert not hasattr(type(guard), "_kernel_ref")
def test_wrapped_mock_venue_full_kernel_drive_never_submits():
"""Real ExecutionKernel over a wrapped MOCK venue: an ENTER intent is
refused at the venue boundary; the kernel's venue-submit-failure
rollback converts the refusal into a synthetic REJECT (slot returns to
IDLE) and the inner venue NEVER sees the order."""
from prod.clean_arch.dita_v2.launcher import build_launcher_bundle
from prod.clean_arch.dita_v2.contracts import (
KernelCommandType,
KernelIntent,
TradeSide,
TradeStage,
)
from datetime import datetime, timezone
bundle = build_launcher_bundle(venue_mode="MOCK", max_slots=1)
kernel = bundle.kernel
inner = kernel.venue
submitted = []
orig_submit = inner.submit
inner.submit = lambda *a, **k: submitted.append(a) or orig_submit(*a, **k)
kernel.venue = ObserveOnlyVenue(inner)
intent = KernelIntent(
timestamp=datetime.now(timezone.utc),
intent_id="i-1", trade_id="T-OBS-1", slot_id=0,
asset="BTCUSDT", side=TradeSide.SHORT,
action=KernelCommandType.ENTER,
reference_price=100.0, target_size=1.0, leverage=1.0,
exit_leg_ratios=(1.0,), reason="test", metadata={},
stage=TradeStage.INTENT_CREATED,
)
outcome = kernel.process_intent(intent)
assert submitted == [] # nothing reached the venue
assert kernel.slot(0).is_free() # FSM rolled back to IDLE
assert any(
"VENUE_SUBMIT_ERROR" in str(e.reason)
for e in outcome.emitted_events
), "expected the synthetic REJECT from the guard refusal"
if __name__ == "__main__":
raise SystemExit(pytest.main([__file__, "-v"]))