C2 (over-fill): after cancel, fetch `order_status` for the authoritative post-cancel fill qty instead of the stale place-response; maker may keep filling during the sleep so the stale qty would size the IOC too large. C1 (false-halt): accumulate BOTH the maker AND the IOC fills into `intended` so reconciliation sees the full executed quantity and does not false-halt on the IOC leg. I3 (dry-run observability): `block_reason` is now populated and returned even when `execute=False`, so monitoring can see gate trips on dry-runs. I4 (arrival mid): capture the true decision-time `book.mid` per symbol while building route inputs; pass that to `_persist` / TCA instead of the stale maker/abort blend. I5 (θ=0 hold): `gross_scaler == 0` (EMA/Kelly warm-up or NaN-recovery) now returns early with `block_reason="zero_gross_scale_hold"` rather than flattening the book. Deliberate exits belong to the kill-switch / crypto-flatten path. M7 (rate-budget window): `RateBudget.try_order` now resets the order counter after a 60-second window so a long-running process never permanently exhausts the per-minute budget. Tests: add FakeExchangePartialMaker + FakeExchangeMakerFillsDuringSleep variants and three new integration tests exercising the full maker→cancel→order_status→IOC path (previously zero coverage); add SELL-above-mid savings case to test_tca; add short position drift + expected-position-fully-closed cases to test_reconciliation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
22 lines
840 B
Python
22 lines
840 B
Python
"""tca.shortfall_bps: signed implementation shortfall of a fill vs the arrival mid, in bps (cost positive)."""
|
|
from __future__ import annotations
|
|
|
|
from fxhnt.domain.execution.tca import shortfall_bps
|
|
|
|
|
|
def test_buy_above_mid_is_positive_cost() -> None:
|
|
assert abs(shortfall_bps("BUY", arrival_mid=100.0, fill_price=100.1) - 10.0) < 1e-6
|
|
|
|
|
|
def test_sell_below_mid_is_positive_cost() -> None:
|
|
assert abs(shortfall_bps("SELL", arrival_mid=100.0, fill_price=99.9) - 10.0) < 1e-6
|
|
|
|
|
|
def test_no_fill_is_zero() -> None:
|
|
assert shortfall_bps("BUY", arrival_mid=100.0, fill_price=0.0) == 0.0
|
|
|
|
|
|
def test_sell_above_mid_is_negative_shortfall() -> None:
|
|
"""Selling above mid is a SAVING (negative cost) — maker rebate / price improvement."""
|
|
assert abs(shortfall_bps("SELL", arrival_mid=100.0, fill_price=100.1) - (-10.0)) < 1e-6
|