Files
sentiment-engine/prod/exec_unified/contract.py
Codex 154234eea6 exec(unified): L2 foundation — contract §2 + whether-maker Router §6, pure, 26 tests
First code of the Unified Execution Layer (SPEC_UNIFIED_EXEC_LAYER_20260714.md). Pure
policy, stdlib+Decimal only, zero I/O, zero venue knowledge, zero importers elsewhere —
adopting it breaks nothing (freeze-safe; operator unparked the build 2026-07-14).

- contract.py: ExecutionRequest input surface (§2.1) + V-TYPES (Side, UrgencyClass,
  ProtectiveSpec, ExecutionAdvice), frozen, validated-at-construction, illegal states
  unrepresentable. 'an asset, a size, and a prayer'.
- router.py: decide(request) -> RoutingDecision — total pure map of the §6 urgency
  ladder. Encodes A1 adjudication verdict in the architecture: Router=whether-maker,
  SmartPlacer=where-in-book via the wants_placement/pre_submit seam. Complementary.
- _constants.py: policy magnitudes with provenance; PROVISIONAL ones flagged for
  L8/L10 calibration (never vibes, never a hardcoded cadence).
- test_exec_unified.py: 26 behaviour + mutation-litmus tests. Verified RED under
  CATASTROPHIC->MAKER and ACQUIRE cross_on_expiry->True mutations.

Not yet wired: PINK drive-loop port (§7), venue dialect (§11), telemetry (§12).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:00:42 +02:00

139 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""The Unified Execution Layer contract — "an asset, a size, and a prayer".
This is the WHOLE input surface (spec §2.1). Nothing about the caller's world may
leak in: not leverage math, not postures, not capital, not why-now. Every brain the
factory produces (BLUE, UV/BLUE-PRIME, VIOLET, VIBRASS children, MALKHUT) executes
through the same body by speaking only this vocabulary.
Pure stdlib + Decimal. Zero I/O, zero venue knowledge. V-TYPES discipline: frozen,
validated at construction, illegal states unrepresentable.
Provenance: prod/docs/SPEC_UNIFIED_EXEC_LAYER_20260714.md §2, §6.
Home-to-be: prod/clean_arch/dita_v2/exec/ (vendored). Lives standalone until the
vendor flow relocates it — do not import from the vendored dita_v2 tree yet.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from decimal import Decimal
from enum import Enum
class Side(Enum):
BUY = "BUY"
SELL = "SELL"
class UrgencyClass(Enum):
"""The caller's declaration of *why* — the only "why" this layer ever learns (§6).
Ordered most-urgent → least. The ordinal is meaningful: dispatch priority and the
"never delay a more-urgent order" invariant read it (spec §9 priority ladder).
"""
CATASTROPHIC = 0 # SL / kill / liquidation — taker MARKET now, no cleverness ever
PROTECT = 1 # TP_FLOOR give-back, ADVSL retract — maker@touch, 1 reprice, then cross
HARVEST = 2 # FIXED_TP — maker at target ± offset, chase ≤N, give up on regression
ROTATE = 3 # MAX_HOLD / admin — patient maker, TTL from deadline_ms, cross at TTL
ACQUIRE = 4 # ENTER — patient maker inside spread, abandon (never chase)
@property
def is_exit(self) -> bool:
"""Exit-side urgencies decrease a position; entries (ACQUIRE) increase it.
Exits may only ever tighten toward urgency, never reprice away (spec §4-2).
"""
return self in (UrgencyClass.CATASTROPHIC, UrgencyClass.PROTECT,
UrgencyClass.HARVEST, UrgencyClass.ROTATE)
@dataclass(frozen=True)
class ProtectiveSpec:
"""Attach-a-dead-man's-stop geometry (spec §10). Mark-based, rides the entry order.
Either an explicit ``stop_price`` OR a ``stop_mult`` multiple of the caller's
software SL distance — never both, never neither. The venue-attached STOP_MARKET
fires only when the client is dead or blind (HZ silent-death lineage): parity-
invisible, because it only acts once the software layer has already failed.
"""
stop_price: Decimal | None = None
stop_mult: Decimal | None = None # × software-SL distance (e.g. 2.0)
working_type: str = "MARK_PRICE" # venue geometry truth = mark, never last
reduce_only: bool = True
def __post_init__(self) -> None:
has_px = self.stop_price is not None
has_mult = self.stop_mult is not None
if has_px == has_mult:
raise ValueError("ProtectiveSpec needs exactly one of stop_price / stop_mult")
if has_px and self.stop_price <= 0:
raise ValueError(f"stop_price must be positive, got {self.stop_price}")
if has_mult and self.stop_mult <= 0:
raise ValueError(f"stop_mult must be positive, got {self.stop_mult}")
if self.working_type != "MARK_PRICE":
# Geometry truth is the mark (spec §9 three-price-truths). Guardrail, not law.
raise ValueError("protective working_type must be MARK_PRICE")
@dataclass(frozen=True)
class ExecutionAdvice:
"""MALKHUT/other advisor hints — an advisor, never an authority (spec §5, §11-L11).
The layer may take these hints or ignore them; CATASTROPHIC ignores them *by law*.
Advice can never change WHAT was asked (side/size/asset/reduce_only are sacred).
"""
source: str = "unknown" # who advised (provenance, non-authoritative)
prefer_post_only: bool | None = None # hint: rest as maker if possible
qty_fraction: Decimal | None = None # hint: work this fraction per slice (slicer above)
note: str = ""
def __post_init__(self) -> None:
if self.qty_fraction is not None and not (Decimal(0) < self.qty_fraction <= Decimal(1)):
raise ValueError(f"qty_fraction must be in (0,1], got {self.qty_fraction}")
@dataclass(frozen=True)
class ExecutionRequest:
"""The whole surface. Nothing else may leak in (spec §2.1).
``guideline_price`` is a decision-time *reference*, NOT a limit — it resolves the
ADVSL stale-price concern: scan-clock exits pass the price that triggered them; the
drive loop executes against the live book, never against this frozen number.
"""
request_id: str # caller-minted; idempotency key end-to-end
asset: str # canonical undashed ("BTCUSDT"); dialect dashes it
side: Side
size: Decimal # base quantity; sizing is the caller's solved problem
urgency: UrgencyClass # assigned by CALLER, NEVER inferred here
reduce_only: bool = False # position-decreasing intent (exits set True)
guideline_price: Decimal | None = None # reference, not a limit
protective: ProtectiveSpec | None = None
advice: ExecutionAdvice | None = None
deadline_ms: int | None = None # caller's patience budget (ROTATE/ACQUIRE)
def __post_init__(self) -> None:
if not self.request_id:
raise ValueError("request_id is mandatory (idempotency key)")
if self.asset != self.asset.upper() or "-" in self.asset:
raise ValueError(f"asset must be canonical undashed upper, got {self.asset!r}")
if self.size <= 0:
raise ValueError(f"size must be positive, got {self.size}")
if self.guideline_price is not None and self.guideline_price <= 0:
raise ValueError(f"guideline_price must be positive, got {self.guideline_price}")
if self.deadline_ms is not None and self.deadline_ms <= 0:
raise ValueError(f"deadline_ms must be positive, got {self.deadline_ms}")
# Contract invariant: an entry (increases position) can't be reduce_only.
if self.reduce_only and self.urgency is UrgencyClass.ACQUIRE:
raise ValueError("ACQUIRE (entry) cannot be reduce_only")
@property
def client_order_id_seed(self) -> str:
"""Deterministic seed for the venue clientOrderId (promise #4: never lose an order).
Prefix discipline lives in the dialect layer (u-/m-); this is the stable core.
"""
return self.request_id