Repository navigation
Description
Ledger limit check halts falsely on rounded cumulative averages at the limit, and the obvious fix can miss real violations
Found by an independent pre-merge review of #205 (Codex, different model family) and a delta review. It was reproduced in tests/test_adaptive_paper_safety.py probes. Lane: trading.
The false halt (current main)
safety.Ledger.record_order derives the new shares' price from two cumulative averages, (filled * avg - prior_qty * prior_avg) / delta, and halts permanently with incremental_fill_violates_limit when that price crosses the limit. Alpaca rounds cumulative averages to six decimals (observed 2026-09-24: 15.000833 for 11 x 15.00 + 1 x 15.01). Two examples:
- Buy 1 x 100.00, then 2 x 100.01, at a 100.01 limit. Reported 100.006667, derived 100.0100005 > 100.01, so the ledger halts.
- Sell 1 x 100.01, then 2 x 100.00, at a 100.00 limit. Reported 100.003333, derived 99.9999995 < 100.00, so the ledger halts.
Exposure today is low:
- Mover entries and exits are marketable limits 50 bps beyond the quote, so their fills do not land on the limit.
- Adaptive-lane orders are 1 share, so they have no multi-price partials.
It matters for larger orders at tight limits.
Why "halt only on a certain violation" is not enough
Loosening the check by the reporting bound (filled x 0.000001 + prior_qty x 0.000001) lets a real one-tick violation pass once that bound reaches a tick:
- Buy limit 0.9354; 99 x 0.9354, then 1 x 0.9355.
- Reported 0.935401; delta notional 0.9355; bound 0.000199.
- Not halted, although 0.9355 > 0.9354.
That version was in #205 and was reverted there (566a823).
A direction that handles both (not implemented)
- Resolve each cumulative notional separately on the whole-share execution grid (0.0001), within
filled x 0.000001offilled x avg. The delta is the difference of the two resolved values. - When both are unique, compare exactly. That covers every example above: the two false halts pass and the 0.9355 violation halts. The ledger could then also book exact notionals instead of drifting by up to
filled x 0.000001(300.020001 versus 300.02). - When either is ambiguous (for example 150 shares on a 4-decimal grid), choose explicitly between halting on a possible violation (false halts) and halting only on a certain one (missed violations).
- Fractional quantities break the grid assumption. The adaptive lane and
test_nine_decimal_partial_fill_can_recover_exact_residualuse them, so keep the current derivation for them.
blueprints/us-equities/adaptive-paper/fills.py (in #205) has the adapter-side resolution, report_unit and the tests to build on.
Activity