Skip to content

fix: symbol-case position lookup, shadow order ids, Alpaca short sign and zero fills, bar windows - #112

Closed
quantum-like wants to merge 5 commits into
ml4t:mainfrom
quantum-like:review/fixes
Closed

quantum-like wants to merge 5 commits into
ml4t:mainfrom
quantum-like:review/fixes

Conversation

@quantum-like

Copy link
Copy Markdown

Owning issue

Closes #111

Outcome

5 correctness fixes, one commit each, each with a focused test:

  • Position lookup on the safety facade and the strategy wrapper used the caller's casing, so close_position("aapl") left an AAPL position open. Lookups now use the same uppercase symbol as the broker adapters. Test: tests/unit/test_safe_broker.py::test_position_lookup_and_close_ignore_symbol_case, ::test_external_position_lookup_ignores_symbol_case. (f95d914)
  • Shadow order ids were millisecond timestamps, so two accepts in the same millisecond collided in reconciliation. Ids now come from a per-broker sequence. Test: tests/unit/test_safe_broker.py::test_shadow_order_ids_stay_unique_when_the_clock_does_not_advance. (bd6a6a2)
  • Alpaca reports an unsigned qty plus a separate side, so short positions were stored as longs and a later close sold into the short. Shorts are now stored with a negative quantity. Test: tests/unit/test_alpaca_broker.py::test_sync_positions_signs_short_holdings. (dc84667)
  • An Alpaca fill event with filled_qty 0/absent was recorded as a complete fill. It is now rejected. Test: tests/unit/test_alpaca_broker.py::test_fill_event_does_not_invent_a_quantity. (e5a518f)
  • Aggregated bars whose length does not divide 60 minutes (e.g. 90 minutes) restarted every hour. Windows now align to midnight; hour-dividing sizes are unchanged. Test: tests/unit/test_bar_aggregator.py::test_bar_windows_do_not_restart_at_the_hour. (8fd83f5)

Compatibility

  • No public compatibility impact
  • Compatibility impact is documented in the owning issue and migration guidance

No API changes. Behavior changes only in the cases above.

Verification

  • Tests reproduce the prior failure or verify the requested behavior (each listed test fails on main and passes with its fix)
  • Ruff lint and format checks pass (ruff format --check is clean on the changed files and ruff check reports nothing new in them versus main, but I did not run the full pre-commit suite)
  • ty passes (not run on this branch)
  • Test suite passes: pytest: 1050 passed, 30 skipped before; 1056 passed, 30 skipped after. The skipped tests need external/paper/stress/benchmark flags and were left off, so no broker or live feed was touched.
  • Package build passes when applicable (not run)
  • Strict MkDocs build passes when documentation is affected (no documentation changes)
  • Ecosystem qualification passes (not run)

Documentation and release

No documentation changes. The behavior changes above may be worth a line in the next patch-release notes.

Amir

cursoragent and others added 5 commits October 7, 2026 07:31
SafeBroker and the strategy wrapper looked up positions with the caller's
original casing, while adapters store AAPL-style keys. A lowercase close
therefore returned no position and left the holding open.

Co-authored-by: quantum-like <quantum-like@users.noreply.github.com>
Shadow fills used a millisecond timestamp as the order id. Two orders
accepted in the same millisecond shared an id, so later reconciliation
overwrote the first fill.

Co-authored-by: quantum-like <quantum-like@users.noreply.github.com>
Alpaca position snapshots report an unsigned share count and a separate
long/short side. Treating qty as a signed quantity stored shorts as longs,
so a later close sold more instead of buying to cover.

Co-authored-by: quantum-like <quantum-like@users.noreply.github.com>
A fill event with a zero or missing filled_qty was rewritten to the full
order size. That recorded a complete fill the venue had not reported.

Co-authored-by: quantum-like <quantum-like@users.noreply.github.com>
Bar starts were truncated with minute-of-hour arithmetic. A size that does
not divide 60, including 90-minute bars, restarted every hour and split one
window into two.

Co-authored-by: quantum-like <quantum-like@users.noreply.github.com>
@stefan-jansen

Copy link
Copy Markdown
Contributor

The five repairs from this contribution are retained and completed in the combined maintenance PR #121, including the original quantum-like/Cursor Agent credit and the failing-base/passing-fix evidence. Closing this PR as superseded avoids duplicate integration work; issue #111 remains open and is linked for closure when #121 actually merges. The original contributor branch is preserved. This closure does not claim the repairs have landed.

stefan-jansen added a commit that referenced this pull request Oct 18, 2026
Integrate the broker, aggregation and guide repairs as one maintainer-directed batch.

Original repairs were contributed by quantum-like in PR #112. Preserve the public attribution and retained original commit history.

Closes #111.
Closes #114.
Closes #116.
Closes #117.
Closes #118.
Closes #119.
Closes #120.
Closes #126.
Closes #128.
Closes #132.

Provider qualification and the separate publication decision remain tracked by #92.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compatibility: affected Has Python, platform, dependency, or cross-library compatibility impact priority: normal Normal planned work status: pending-review Acknowledged and waiting for the stated review point type: bug Confirmed or reported incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Lowercase close misses position, shadow order id collisions, Alpaca shorts stored as longs, zero-qty fills, 90-minute bars restart hourly

3 participants