fix(qt): report the price-limit gate input that was actually used - #76
Merged
Conversation
PR #75 moved the I5b gate from the execution bar's close to the price that executes -- the bar VWAP under the new default basis -- but left both report writers asserting "the selected execution-minute raw 1min close vs the raw stk_limit band". Every run since has shipped a report describing a check it did not perform, which is worse than shipping no description at all. The prose is now derived from the active execution_price_basis and states why the executed price is the faithful input: a limit-up minute is either LOCKED (every print at the limit, so the VWAP equals it up to rounding and the buy must be blocked) or OPENED (prints landed below it, direct evidence a fill was achievable, so the buy must go through). The bar close misclassifies both edges. Three facts the runs already computed but never disclosed now reach the reader: opened_limit_up_minutes / opened_limit_down_minutes -- the entire set on which this gate and a close-based gate disagree. A change in what does or does not trade must never be silent. missing_adj_factor_pairs -- holding periods dropped because adj_factor was unusable at an anchor. Non-zero means measured coverage is short; it is never defaulted to 1.0, which would reintroduce the ex-date bias PR #75 removed. The tail report additionally states that returns are corporate-action adjusted and gives the identity, since "exec-to-exec" alone no longer describes them. The wording is extracted into limit_basis_lines() so it is testable rather than buried in a 200-line writer, and pinned by three tests. Mutation evidence: reverting the prose to the close-based claim fails 2 of them; dropping the new counters from the group table fails the third. Note for anyone extending tests/test_i5b_execution_feasibility.py: its fixtures price each execution bar with volume=1.0 and amount=close, so VWAP == close. That kept the pre-PR#75 assertions valid on both bases -- and made them blind to which one the gate reads. The new tests cover that distinction directly. Also sets the I5f diagnostic notional to 1,000,000 RMB, the operator's stated capital ceiling. At 10,000,000 the diagnostic reported that about half of all single-minute desired trades exceeded capacity; that described a portfolio an order of magnitude larger than any this project will run. Re-measured at the real size, no trade falls below capacity (min ratio 1.217, median 11.037).
…t class Review of the first pass found the SAME stale wording surviving in _write_report()'s "Limitations" section, untouched by that commit and identical to what shipped before PR #75. Every I5b/I5c/I5f report would therefore have carried two contradictory descriptions of one check in a single document: the corrected one mid-report and the pre-#75 one at the end. The dev-facing _load_price_limits docstring had it too. Both now derive from the active execution_price_basis. The more useful half is why the first pass missed it. None of the three tests render _write_report at all -- they exercise limit_basis_lines() in isolation and _feasibility_lines() through a stub -- so that location was structurally unreachable by them, and enumerating the places I happened to know about was never going to converge. The new test scans both report modules for any phrasing that asserts the gate compares a CLOSE, so an (N+1)th copy fails rather than ships. "bar_close" the basis name and "that bar's single closing tick" the basis description are legitimate and deliberately not matched. Mutation evidence: restoring the stale Limitations sentence fails the guard, and it names the offending line: AssertionError: intraday_tail_framework.py still claims the price-limit gate compares a CLOSE. ... Derive the wording from execution_price_basis instead of restating it: line 966: f"execution-minute close to raw limits (see the section " pytest 1732 passed (1731 + 1), ruff clean, phase0 unchanged.
This was referenced Jul 21, 2026
StackOverFlow11
added a commit
that referenced
this pull request
Jul 21, 2026
… rebasis PR number (#79) Both follow-ups now have real numbers, read from gh pr list rather than assumed: #78 is fix/limit-basis-single-source and #79 is feat/exec-to-exec-rebasis, both OPEN. The rebasis entry no longer says "PR not yet open". - New entry for #78: #76's regex guard claimed no further copy of the wrong gate description could ship, and review disproved it by running seven plausible rewordings through the actual regex — all seven escape. The fix is structural: limit_basis_phrase() is the one authored sentence and three sites compose it; rendered text is unchanged; the regex stays with its scope corrected to catching a literal revert. - Sharpened the second defect-class rule accordingly: the rule is not "assert no (N+1)th copy exists" — that is precisely what the regex failed to do — it is author the claim once and have every other site compose it. A regex cannot assert that no other sentence makes a claim; having no other sentence can. - #79 entry gains its number, the reason for the rebasis (a minute factor is fixed at 14:50 and the only modellable fill is the 14:51 bar, so close-to-close credits a closing auction this project cannot simulate), the two coverage denominators kept apart as the PR keeps them, and the correctness acceptance. Sources: gh pr view 78, gh pr view 79, RESULTS_post_pr75_2026-07-21.md.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
PR #75 moved the I5b price-limit gate from the execution bar's close to the price that executes (the bar VWAP under the new default basis). It updated the execution-price-basis lines in both report writers — and missed the line that describes the gate itself. Both writers kept asserting:
Every run since has shipped a report describing a check it did not perform. In a project whose standing rule is 绝不静默降级, a report that misstates its own method is worse than no report.
runtime/behaviour is unchanged by this PR. This is a disclosure fix plus three counters that were already being computed and thrown away.What changed
The corrected claim is derived from the active
execution_price_basisand states why the executed price is the faithful input: a limit-up minute is either LOCKED (封死涨停 — every print at the limit, so the VWAP equals it up to rounding and the buy must be blocked) or OPENED (盘中打开 — prints landed below it, direct evidence a fill was achievable, so the buy must go through). The bar close misclassifies both edges.Three facts the runs already computed but never disclosed:
opened_limit_up_minutes/opened_limit_down_minutesmissing_adj_factor_pairsadj_factorwas unusable at an anchor. Non-zero means measured coverage is short. Never defaulted to 1.0 — that would reintroduce the ex-date bias PR #75 removed.The tail report additionally states that returns are corporate-action adjusted and gives the identity, since "exec-to-exec" alone no longer describes them.
Testability
The wording is extracted into
limit_basis_lines()rather than left buried in a 200-line writer, and pinned by three tests.Mutation evidence (actually run, not asserted):
Why the existing I5b tests did not catch this: their fixtures price each execution bar with
volume=1.0, amount=close, so VWAP == close exactly. That kept every pre-PR#75 assertion valid on both bases — and made them structurally blind to which one the gate reads. Recorded in the module docstring so the next person does not rebuild the same blind spot.I5f re-measured at the operator's real capital
portfolio_notional10,000,000 → 1,000,000 RMB, the stated capital ceiling.The earlier "about half of single-minute desired trades exceed capacity" finding described a portfolio an order of magnitude larger than any this project will run. At the real size, single-minute capacity is not binding on this universe/window. This re-scopes the motivation for a future partial-fill / volume-cap layer; it does not retire it, and it is a diagnostic, not a tradability claim.
Verified NAV decomposition (relevant to reading any I5x number)
Same window, three configurations, deterministic:
bar_closefills, unadjusted returns0.976448bar_closefills, adjusted returns1.021137(+4.47pp)bar_vwapfills, adjusted returns (new default)1.019318(−0.18pp)The move is almost entirely the bug fix — ex-dividend drops were being booked as losses, and this window straddles the A-share annual ex-dividend peak. The VWAP fill basis itself is very slightly worse, which is the expected direction. Not a performance improvement, and it must not be read as one.
opened_limit_*are 0/0 in this window, so none of the change came from a relaxed gate; the one up-limit blocked buy is still blocked.Gates
pytest1731 passed (1728 baseline + 3) ·ruffclean · phase0ic_mean=0.9600, annual_return=0.8408· 31/31 configs validate · secret scan 0 hits (token value,.config.json)