Skip to content

Ledger limit check halts falsely on rounded averages at the limit; a bound-only fix misses real violations #211

Open

Description

@seathatflowsinourveins

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.000001 of filled 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_residual use 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    lane:tradingTrading lane: sim, paper and live north star

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions