Files
fxhnt/tests/unit/test_tca.py
jgrusewski abdc9b8dc8 fix(exec): capital-risk fixes — authoritative IOC sizing, accumulate all legs, θ=0 hold, true arrival mid, dry-run gate report, rate-budget window
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>
2026-06-14 17:29:37 +02:00

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