fix: deduplicate monthly Russell execution - #350
Conversation
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eafa5d0ead
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if execution.get(field) not in (None, ""): | ||
| summary[field] = execution[field] |
There was a problem hiding this comment.
Keep no-order monthly cycles eligible for retry
Only persist this execution identity when the cycle actually placed or previewed an order. As written, a Russell cycle with action_done=False and no skip logs—such as the zero-equity path in _normalize_to_value_target_decision—is saved as a successful report with the snapshot-derived signal_date; _report_matches_execution explicitly treats such clean no-action reports as completed, so if the account is funded later in the same three-day window, has_prior_execution_report suppresses the rebalance for that snapshot even though _should_record_execution_marker deliberately did not record a marker.
Useful? React with 👍 / 👎.
Summary
Root cause
The Russell monthly strategy exposed
snapshot_as_ofbut notsignal_date. The LongBridge duplicate marker builder therefore received an empty execution identity and skipped marker reads/writes, allowing every day in the three-day execution window to submit another rebalance.Behavior
After the first successful order submission records the monthly snapshot identity, later scheduler invocations for the same snapshot are skipped. A cycle that submits no order still does not record a marker and remains eligible for a later retry.
Dependency note
CI resolves QuantPlatformKit
mainand requires downstream pins to match its canonicalQPK_PIN. The original PR run failed because this repository still pinned92458590a463; the PR now uses the required9618b4bd8e17. Full LongBridge tests were rerun against that exact pin.Validation
uv run --frozen pytest -q tests/test_strategy_runtime.py tests/test_decision_mapper.py tests/test_rebalance_service.py tests/test_execution_state.py tests/test_request_handling.py(100 passed)uv run --no-sync pytest -q tests(315 passed, 15 subtests passed) against QPK9618b4bd8e17uv run --no-sync ruff check --exclude external .uv run --no-sync python scripts/check_qpk_pin_consistency.pyuv lock --checkgit diff --check