VIOLET V2c: synthetic intent scripts + ExecStormHarness + 9-scenario matrix

synthetic_intents.py: seeded IntentScriptSpec -> CycleSpec scripts
(script_hash + outcomes_hash determinism, V0 discipline); per-scenario
router ExecConfig constructed directly (no env mutation); cycle executor
runs full ENTER->terminal->flatten lifecycles with per-scenario terminal
predicates and cycle-end invariants (working registry empty, driver
drained, slot flat).

exec_harness.py: composition root — production bundle (MOCK, injected
ScriptedVenue), ExecDeadlineDriver ports wired, pump = venue.reconcile()
-> kernel + driver.on_fill forwarding (the production seam), gate report
via the ExecGateReport schema, archive next to V0 reports.

scripted_venue.py amendment: MARKET orders never rest (venue realism —
directives are keyed by trade_id and the R1 MARKET fallback shares the
position's trade_id).

Matrix green through the REAL kernel at 100ms TTL: immediate fill,
rest-then-fill (deadline cancelled), fill-races-cancel (no retry),
rest-expire-retry (-r1 opens), retry-exhaust skip|market, exit-expire ->
MARKET same trade_id, post-only reject, cancel-reject (no strand). Two
runs same seed -> identical outcomes_hash. Router 77 green; shared clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-13 00:31:09 +02:00
parent dfe7136404
commit 7ae49c587e
5 changed files with 773 additions and 1 deletions

View File

@@ -0,0 +1,374 @@
"""VIOLET V2: seeded synthetic intent scripts + the cycle executor.
Each cycle is one full trade lifecycle (ENTER → terminal → flatten) driven
through the REAL kernel + ScriptedVenue + ExecDeadlineDriver, with the
scenario controlling venue behavior and router policy. Scripts are seeded
and hashed (same discipline as the V0 storm): same seed ⇒ same scenario
sequence, prices, trade ids — and the per-cycle outcome tuples are part of
the determinism check in the V2 gate.
"""
from __future__ import annotations
import asyncio
import hashlib
import json
import random
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional
from prod.clean_arch.dita_v2.contracts import (
KernelCommandType,
KernelEventKind,
KernelIntent,
TradeSide,
TradeStage,
)
from prod.clean_arch.dita_v2.exec_router import ExecConfig
from .clock import mono_ns
from .exec_driver import SLOT_OPENISH
from .scripted_venue import Directive
EXIT_URGENT_REASON = "CATASTROPHIC" # always MARKET per router policy
class Scenario(str, Enum):
IMMEDIATE_FILL = "immediate_fill"
REST_THEN_FILL = "rest_then_fill"
FILL_RACES_CANCEL = "fill_races_cancel"
REST_EXPIRE_RETRY = "rest_expire_retry"
RETRY_EXHAUST_SKIP = "retry_exhaust_skip"
RETRY_EXHAUST_MARKET = "retry_exhaust_market"
EXIT_EXPIRE_MARKET = "exit_expire_market"
POST_ONLY_REJECT = "post_only_reject"
CANCEL_REJECT = "cancel_reject"
def router_config_for(scenario: Scenario) -> ExecConfig:
"""Constructed DIRECTLY (no env mutation) per the V2 plan."""
if scenario == Scenario.REST_EXPIRE_RETRY:
return ExecConfig(style="maker_both", entry_miss="retry",
entry_retries=1, retry_exhaust="skip")
if scenario == Scenario.RETRY_EXHAUST_SKIP:
return ExecConfig(style="maker_both", entry_miss="retry",
entry_retries=1, retry_exhaust="skip")
if scenario == Scenario.RETRY_EXHAUST_MARKET:
return ExecConfig(style="maker_both", entry_miss="retry",
entry_retries=1, retry_exhaust="market")
return ExecConfig(style="maker_both", entry_miss="skip")
@dataclass
class IntentScriptSpec:
n_cycles: int = 18 # 2 × the 9-scenario matrix
seed: int = 7
asset: str = "STORMUSDT"
base_price: float = 100.0
walk_bps: float = 8.0
scenarios: Optional[List[Scenario]] = None # None → round-robin all
def to_dict(self) -> Dict[str, Any]:
return {
"n_cycles": self.n_cycles, "seed": self.seed, "asset": self.asset,
"base_price": self.base_price, "walk_bps": self.walk_bps,
"scenarios": [s.value for s in (self.scenarios or list(Scenario))],
}
@dataclass(frozen=True)
class CycleSpec:
idx: int
scenario: Scenario
trade_id: str
price: float
@dataclass
class CycleOutcome:
idx: int
scenario: str
ok: bool
entry_path: str # base | r1 | m | none
exit_path: str # maker | market_fallback | urgent_market | none
detail: str = ""
resolution_ms: float = 0.0
def key(self) -> Dict[str, Any]:
"""Determinism-relevant projection (latency excluded)."""
return {"idx": self.idx, "scenario": self.scenario, "ok": self.ok,
"entry_path": self.entry_path, "exit_path": self.exit_path}
def generate_script(spec: IntentScriptSpec) -> List[CycleSpec]:
rng = random.Random(spec.seed)
order = spec.scenarios or list(Scenario)
px = spec.base_price
out: List[CycleSpec] = []
for i in range(spec.n_cycles):
px = max(0.01, px * (1.0 + rng.uniform(-1, 1) * spec.walk_bps / 1e4))
out.append(CycleSpec(idx=i, scenario=order[i % len(order)],
trade_id=f"vsyn-{spec.seed}-{i:05d}",
price=round(px, 6)))
return out
def script_hash(cycles: List[CycleSpec]) -> str:
payload = json.dumps(
[{"idx": c.idx, "scenario": c.scenario.value,
"trade_id": c.trade_id, "price": c.price} for c in cycles],
sort_keys=True, separators=(",", ":")).encode()
return hashlib.sha256(payload).hexdigest()
def outcomes_hash(outcomes: List[CycleOutcome]) -> str:
payload = json.dumps([o.key() for o in outcomes],
sort_keys=True, separators=(",", ":")).encode()
return hashlib.sha256(payload).hexdigest()
# ── intent builders (mirror pink_direct's plan→intent mapping) ────────────────
def build_enter_intent(cycle: CycleSpec, plan: Any, asset: str) -> KernelIntent:
return KernelIntent(
timestamp=datetime.now(timezone.utc),
intent_id=cycle.trade_id, trade_id=cycle.trade_id, slot_id=0,
asset=asset, side=TradeSide.SHORT,
action=KernelCommandType.ENTER,
reference_price=cycle.price, target_size=1.0, leverage=1.0,
exit_leg_ratios=(1.0,), reason=f"vsyn:{cycle.scenario.value}",
metadata={"_time_in_force": ("PostOnly" if plan.post_only else "GTC"),
"_exec_reason": plan.reason},
stage=TradeStage.INTENT_CREATED,
order_type=plan.order_type,
limit_price=float(plan.limit_price or 0.0),
)
def build_exit_intent(trade_id: str, plan: Any, asset: str, *,
size: float, price: float) -> KernelIntent:
return KernelIntent(
timestamp=datetime.now(timezone.utc),
intent_id=f"{trade_id}-x", trade_id=trade_id, slot_id=0,
asset=asset, side=TradeSide.SHORT,
action=KernelCommandType.EXIT,
reference_price=price, target_size=float(size), leverage=1.0,
exit_leg_ratios=(1.0,), reason=plan.reason,
metadata={"_time_in_force": ("PostOnly" if plan.post_only else "GTC"),
"_exec_reason": plan.reason},
stage=TradeStage.INTENT_CREATED,
order_type=plan.order_type,
limit_price=float(plan.limit_price or 0.0),
)
# ── cycle executor ────────────────────────────────────────────────────────────
class SyntheticIntentDriver:
"""Runs scripted cycles against (kernel, venue, exec driver, router).
The owner (ExecStormHarness) provides ``pump`` — which MUST forward
working-order fills to ``driver.on_fill`` — plus slot_view (same shape
as the exec driver's port).
"""
def __init__(self, *, kernel: Any, venue: Any, driver: Any,
pump, slot_view, ttl_ms: float, logger: Any = None) -> None:
self.kernel = kernel
self.venue = venue
self.driver = driver
self.pump = pump
self.slot_view = slot_view
self.ttl_ms = float(ttl_ms)
self.logger = logger
# — helpers —
def _slot_flat(self) -> bool:
_tid, stage, size = self.slot_view()
return size <= 0.0 and stage not in SLOT_OPENISH and stage not in (
"ORDER_SENT", "ORDER_ACKED", "ENTRY_WORKING", "ORDER_REQUESTED",
"EXIT_REQUESTED")
async def _await_terminal(self, pred, timeout_s: float) -> bool:
deadline = mono_ns() + int(timeout_s * 1e9)
while mono_ns() < deadline:
await self.pump()
if pred():
return True
await asyncio.sleep(0.005)
return False
def _arm_venue(self, cycle: CycleSpec) -> None:
tid, s = cycle.trade_id, cycle.scenario
ttl = self.ttl_ms
if s == Scenario.IMMEDIATE_FILL:
self.venue.set_directive(tid, Directive.IMMEDIATE_FILL)
elif s == Scenario.REST_THEN_FILL:
self.venue.set_directive(tid, Directive.REST_THEN_FILL,
fill_delay_ms=ttl * 0.3)
elif s == Scenario.FILL_RACES_CANCEL:
self.venue.set_directive(tid, Directive.FILL_RACES_CANCEL)
elif s == Scenario.REST_EXPIRE_RETRY:
self.venue.set_directive(tid, Directive.REST_THEN_EXPIRE)
self.venue.set_directive(f"{tid}-r1", Directive.IMMEDIATE_FILL)
elif s in (Scenario.RETRY_EXHAUST_SKIP,):
self.venue.set_directive(tid, Directive.REST_THEN_EXPIRE)
elif s == Scenario.RETRY_EXHAUST_MARKET:
self.venue.set_directive(tid, Directive.REST_THEN_EXPIRE)
# -m fallback is MARKET → fills via venue realism, no directive.
elif s == Scenario.EXIT_EXPIRE_MARKET:
self.venue.set_directive(tid, Directive.IMMEDIATE_FILL)
# re-armed to REST_THEN_EXPIRE after the entry fills (same tid).
elif s == Scenario.POST_ONLY_REJECT:
self.venue.set_directive(tid, Directive.POST_ONLY_REJECT)
elif s == Scenario.CANCEL_REJECT:
self.venue.set_directive(tid, Directive.CANCEL_REJECT)
async def _flatten(self, cycle: CycleSpec, outcome: CycleOutcome) -> bool:
"""End-of-cycle: close any open position (urgent MARKET) and clear
any stuck working slot (direct CANCEL — operator-style cleanup for
the CANCEL_REJECT scenario)."""
slot_tid, stage, size = self.slot_view()
if size > 0.0 and slot_tid:
router = self.driver.ports.router
plan = router.plan_exit(trade_id=slot_tid, asset="STORMUSDT",
position_side="SHORT",
reference_price=cycle.price,
reason=EXIT_URGENT_REASON)
intent = build_exit_intent(slot_tid, plan, "STORMUSDT",
size=size, price=cycle.price)
await self.kernel.process_intent_async(intent)
if outcome.exit_path == "none":
outcome.exit_path = "urgent_market"
ok = await self._await_terminal(self._slot_flat, 2.0)
if not ok:
return False
elif not self._slot_flat() and slot_tid:
# stuck working entry (cancel-reject path): direct CANCEL, the
# venue accepts the second attempt (directive consumed realism
# is NOT modeled — clear it explicitly instead).
self.venue._scripts.pop(slot_tid, None)
cancel = KernelIntent(
timestamp=datetime.now(timezone.utc),
intent_id=f"{slot_tid}-opcxl", trade_id=slot_tid, slot_id=0,
asset="STORMUSDT", side=TradeSide.SHORT,
action=KernelCommandType.CANCEL,
reference_price=cycle.price, target_size=1.0, leverage=1.0,
exit_leg_ratios=(1.0,), reason="vsyn:cleanup", metadata={},
stage=TradeStage.INTENT_CREATED,
)
await self.kernel.process_intent_async(cancel)
if not await self._await_terminal(self._slot_flat, 2.0):
return False
return True
# — the cycle —
async def run_cycle(self, cycle: CycleSpec) -> CycleOutcome:
out = CycleOutcome(idx=cycle.idx, scenario=cycle.scenario.value,
ok=False, entry_path="none", exit_path="none")
router = self.driver.ports.router
# Per-scenario policy swap — safe at cycle boundaries because the
# working registry is empty between cycles (asserted at cycle end).
router.config = router_config_for(cycle.scenario)
t0 = mono_ns()
self._arm_venue(cycle)
plan = router.plan_entry(trade_id=cycle.trade_id, asset="STORMUSDT",
position_side="SHORT",
reference_price=cycle.price)
if plan.suppress:
out.detail = f"entry suppressed at quiescence: {plan.reason}"
return out
intent = build_enter_intent(cycle, plan, "STORMUSDT")
await self.kernel.process_intent_async(intent)
if plan.is_maker:
self.driver.on_submit(plan, intent)
budget_s = self.ttl_ms / 1000.0 + 1.0
s = cycle.scenario
def _slot_open_with(tid: str) -> bool:
slot_tid, stage, size = self.slot_view()
return slot_tid == tid and size > 0.0 and stage in SLOT_OPENISH
if s in (Scenario.IMMEDIATE_FILL, Scenario.REST_THEN_FILL,
Scenario.FILL_RACES_CANCEL):
if not await self._await_terminal(
lambda: _slot_open_with(cycle.trade_id), budget_s):
out.detail = "entry never opened"
return out
out.entry_path = "base"
elif s == Scenario.REST_EXPIRE_RETRY:
if not await self._await_terminal(
lambda: _slot_open_with(f"{cycle.trade_id}-r1"),
budget_s + self.ttl_ms / 1000.0):
out.detail = "retry never opened"
return out
out.entry_path = "r1"
elif s == Scenario.RETRY_EXHAUST_MARKET:
if not await self._await_terminal(
lambda: _slot_open_with(f"{cycle.trade_id}-m"),
budget_s + 2 * self.ttl_ms / 1000.0):
out.detail = "market fallback never opened"
return out
out.entry_path = "m"
elif s in (Scenario.RETRY_EXHAUST_SKIP, Scenario.POST_ONLY_REJECT,
Scenario.CANCEL_REJECT):
# Terminal = no position, working registry cleared.
def _resolved() -> bool:
_tid, _stage, size = self.slot_view()
return (size <= 0.0 and not router.working_orders()
and not self.driver._resolving)
if not await self._await_terminal(
_resolved, budget_s + 2 * self.ttl_ms / 1000.0):
out.detail = "miss path never resolved"
return out
out.entry_path = "none"
# EXIT phase for every cycle that holds a position.
slot_tid, _stage, size = self.slot_view()
if size > 0.0:
if s == Scenario.EXIT_EXPIRE_MARKET:
out.entry_path = "base" # immediate-fill entry above
self.venue.set_directive(slot_tid, Directive.REST_THEN_EXPIRE)
xplan = router.plan_exit(trade_id=slot_tid, asset="STORMUSDT",
position_side="SHORT",
reference_price=cycle.price,
reason="TAKE_PROFIT")
if not xplan.is_maker:
out.detail = f"expected maker exit, got {xplan.reason}"
return out
xintent = build_exit_intent(slot_tid, xplan, "STORMUSDT",
size=size, price=cycle.price)
await self.kernel.process_intent_async(xintent)
self.driver.on_submit(xplan, xintent)
if not await self._await_terminal(self._slot_flat,
budget_s + 1.0):
out.detail = "exit market fallback never closed"
return out
out.exit_path = "market_fallback"
# all other scenarios flatten below
if not await self._flatten(cycle, out):
out.detail = "flatten failed"
return out
# Cycle-end invariants — the matrix's hard correctness core.
if router.working_orders():
out.detail = f"stuck working orders: {router.working_orders()}"
return out
if not await self.driver.drain(1.0):
out.detail = f"driver did not drain: {self.driver.snapshot()}"
return out
out.resolution_ms = (mono_ns() - t0) / 1e6
out.ok = True
return out
async def run(self, spec: IntentScriptSpec) -> List[CycleOutcome]:
outcomes = []
for cycle in generate_script(spec):
outcomes.append(await self.run_cycle(cycle))
return outcomes