refactor(shadow-account): single-source price-feature names + keep bound precision#316
Conversation
…und precision Follow-up to the HKUDS#314 review (non-blocking items I said I'd take separately): - models: add PRICE_FEATURES on the data-contract boundary; extractor and codegen now import it instead of each repeating the literal tuple, so the names can't drift between extraction and codegen - extractor: round price-feature bounds to 4 decimals — round(.,2) collapsed return-type bounds (prior_5d_return ~ ±0.0x, e.g. 0.0123 -> 0.01) - tests: drift guard (codegen reads the same names the extractor writes) + precision regression Deliberately left out the third review nit (compute RSI/return only when a rule has price bounds): that path runs once per code, not in a hot loop, and gating it would add template branching for negligible gain. Signed-off-by: Robin1987China <41602358+Robin1987China@users.noreply.github.com>
Code Review — 5-Agent Parallel ReviewReviewed by 5 parallel agents: Goal Verification, Code Quality, Security, QA Execution, and Context Mining. Overall Verdict: ✅ PASSED
Goal Achievement
QA Execution Results
Non-blocking Findings1. MINOR — The docstring states "the scanner evaluates them in real time", but Suggested fix — amend the docstring to something like: #: ...codegen flattens their ``{min,max}`` bounds into the template.
#: The scanner currently uses its own feature pipeline and does not
#: directly consume these names.2. MINOR — Precision test assertion is clever but opaque
assert bounds["min"] != round(bounds["min"], 2) or bounds["max"] != round(bounds["max"], 2)requires a mental pause. A direct assertion like 3. NITPICK — Function-local imports in new tests Both new tests use function-local imports ( Follow-up SuggestionConsider a follow-up issue for the scanner gap: Summary: Clean, focused refactor that correctly achieves both stated goals with appropriate test coverage and zero regressions across the full test suite. Ready to merge. |
|
Thanks for the PR — we’ve seen it and will review the current diff as soon as we can. I also noticed the community review above; we’ll take that into account when checking whether this is ready to merge. |
|
Merged via squash in I’ll track the scanner/docstring follow-up separately rather than blocking this PR. |
|
Quick note on the docstring finding before the follow-up — I think the scanner part is already covered, so the docstring is accurate as written.
One nuance worth being precise about: The review was likely run against a tree before #314 landed — easy to miss given the timing. |
Summary
Two small follow-ups from the #314 review (the non-blocking items I said I would take separately): remove a duplicated feature-name list, and stop losing precision on extracted return bounds.
Why
_PRICE_FEATURESinextractor.pyand a literal("entry_rsi14", "prior_5d_return")tuple incodegen.py. Add a feature in one place and forget the other, and codegen silently stops flattening its bounds. They are the contract between extraction and codegen, so they should have one source._cluster_to_rulerounded bounds withround(., 2). RSI tolerates that, butprior_5d_returnsits around ±0.0x, so a 0.0123 bound collapses to 0.01 — the band the generated engine and scanner check loses resolution.Changes
models.py: addPRICE_FEATURESon the data-contract boundary (where the frozen dataclasses already live).extractor.py: alias the local name tomodels.PRICE_FEATURES; round price-feature bounds to 4 decimals.codegen.py: iteratePRICE_FEATURESinstead of the literal tuple.tests: a drift guard (codegen reads exactly the names the extractor writes) and a precision regression for small return bounds.I left out the third review nit — computing RSI/return only when a rule has price bounds. That path runs once per code, not in a hot loop, and gating it would add template branching for negligible gain.
Test Plan
Checklist
shadow_account/)