exec_unified: add placer.py (SmartPlacer pre_submit seam) + mutation-litmus tests

- MarketSnapshot: best_bid, best_ask, spread_bps, tick, step (Decimal, frozen, validated)
- PlacementPlan: limit_price (quantized to tick), post_only=True
- pre_submit: returns None for TAKER, spread gate failure, or quantize-cross; otherwise PlacementPlan
- Spread gate uses live spread_bps (fixes dead _spread_allows_maker)
- Quantize to tick BEFORE returning (ROUND_HALF_EVEN per §4-15)
- Cross-after-quantize guard rejects if tick quantization crosses book
- 33 mutation-litmus tests: spread-gate flip, TAKER gate, cross-after-quantize all go RED
- Router.PROTECT wants_placement=True (at touch)
- Pure stdlib+Decimal, zero I/O/venue
This commit is contained in:
Codex
2026-07-14 23:14:07 +02:00
parent adba9f3fe8
commit d2e6d78abd
3 changed files with 695 additions and 1 deletions

View File

@@ -91,7 +91,7 @@ def decide(request: ExecutionRequest) -> RoutingDecision:
ttl=TtlDiscipline.BOUNDED_MS,
max_ms=K.PROTECT_MAX_MS,
cross_on_expiry=True,
wants_placement=False, # at touch, not an offset model
wants_placement=True, # at touch 2014 SmartPlacer places at best_bid/best_ask
ignore_advice=False,
rationale="PROTECT: maker@touch, 1 reprice, cross at 2s",
)