Skip to content

fix(orders): full-close exit re-expands to grown position under pyramiding#46

Merged
luisleo526 merged 1 commit into
mainfrom
fix/full-close-exit-reexpand-pyramiding
Jun 29, 2026
Merged

fix(orders): full-close exit re-expands to grown position under pyramiding#46
luisleo526 merged 1 commit into
mainfrom
fix/full-close-exit-reexpand-pyramiding

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Summary

A qty_percent=100 strategy.exit re-issued every bar while the position grows via pyramiding/DCA had its reserved quantity frozen at the size from when it was first placed. clear_existing_exit_order captured the prior order's absolute qty into preserved_reserved_qty, and compute_exit_reserved_qty honored it unconditionally — overriding the 100% intent. At the TP touch only the first FIFO lot closed at the true limit; the residual lots exited one bar late at a re-priced limit, fragmenting one logical exit across two bars and producing wrong exit prices for all but the first lot.

Fix (1 logical line)

Gate the preserved-qty carry to genuine partial (qty_percent < 100) re-issues:

if (!std::isnan(preserved_reserved_qty) && qp_io < 100.0 - kFullPercentEps) {

A re-issued full-close exit now falls through to recompute against the full current position (the prior order is already cleared from pending_orders_), so the whole stack closes at the single TP touch. Partial re-issues keep the carry (no double-reserve); the explicit-qty / from_entry bracket path never calls this function and is unaffected.

Gates

  • ctest: 75/75 passed — incl. test_multi_tier_exit_precedence, test_fills_edge, test_accounting_reconciliation; adds test_full_close_while_pyramiding (reverting the gate flips it to 5 failures — the test has teeth).
  • Corpus: verify_corpus --all = 251 excellent / 1 documented anomaly — exactly baseline, zero tier regressions.
  • Minimal repro (3 pyramid lots, 100% TP): all 30 units close at the TP on the trigger bar (PnL 120), not fragmented across two bars (PnL 200).
  • Edge cases reasoned + green: exactly-100% (clamped), >100% (clamped), explicit-qty/OCA brackets, genuine partials, multi-tier precedence.

🤖 Generated with Claude Code

…iding

A qty_percent=100 strategy.exit re-issued every bar while the position grows via
pyramiding had its reserved qty frozen at the size from first issue:
clear_existing_exit_order captured the prior order's absolute qty as
preserved_reserved_qty, and compute_exit_reserved_qty then honored it
unconditionally, overriding the 100% intent. At the TP touch only the first FIFO
lot closed at the true limit; the residual lots exited one bar late at a
re-priced limit. Gate the preserved-qty carry to genuine partial
(qty_percent < 100) re-issues, so a re-issued full-close exit recomputes against
the full current position and closes the whole stack at the single TP touch.

ctest: 75/75 passed (incl. test_multi_tier_exit_precedence, test_fills_edge,
test_accounting_reconciliation); adds test_full_close_while_pyramiding. Corpus:
verify_corpus --all = 251 excellent / 1 documented anomaly, zero tier regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@luisleo526 luisleo526 merged commit f14f201 into main Jun 29, 2026
5 checks passed
@luisleo526 luisleo526 deleted the fix/full-close-exit-reexpand-pyramiding branch June 29, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant