feat(shadow-account): carry price-condition bounds (RSI, prior return) into extracted rules#314
Conversation
Code Review — 5-Agent Parallel ReviewReviewed by 5 parallel agents: Goal Verification, Code Quality, Security, QA Execution, and Context Mining. Overall Verdict: ❌ FAILED (2 blocking issues)
🔴 Blocking Issue 1:
|
| # | Category | Finding | Severity |
|---|---|---|---|
| 1 | Performance | Template generate() unconditionally computes RSI and prior_return for every code, even when the matched rule has no price bounds |
MINOR |
| 2 | Maintainability | Feature name tuple ("entry_rsi14", "prior_5d_return") is hard-coded in 3 places (extractor _PRICE_FEATURES, codegen loop, template methods) — consider a single shared constant |
MINOR |
| 3 | Precision | round(quantile, 2) on prior_5d_return (typical range ±0.0x) loses meaningful precision; consider round(..., 4) for return-type features |
NITPICK |
| 4 | Security Testing | No adversarial injection tests for the codegen pipeline (e.g., crafted rule_id with shell commands) |
MINOR |
| 5 | Docs | PR checklist claims "tasks.md T3.4/T5.7 wording corrected" but tasks.md does not exist in the repository |
FYI |
Positive Observations
- Defense-in-depth code generation safety:
_literal_safe_value(type whitelist) →_python_literal(repr()+ast.literal_evalroundtrip) →validate_generated(ast.parse+ shape check). Three-layer protection against injection — solid. - Clean data flow: extractor → codegen → template key naming is consistent (
entry_rsi14_min/max,prior_5d_return_min/max) - Robust NaN handling: Three layers of protection (
dropna()→len >= 2→isinstancechecks) - Backward compatibility: Rules without price bounds gracefully skip price checks via
rule.get()returningNone - RSI implementation: Correctly mirrors
compute_rsiinexample_signal_engine.py:13per [Feature] Trade Journal -> strategy extraction -> backtest bridge (Phase 4c) - planning question #295 design guidance - Test coverage: 16 new tests — comprehensive coverage of extraction, codegen, template behavior, and security injection
Recommended Fix Priority
- Update
scanner.pyto handle{"min": lo, "max": hi}condition format and add RSI computation — silent regression - Pass
price_featuresto_heuristic_single_rule— one-line fix for behavioral consistency - Consider conditional computation in the template (only compute RSI/return when any rule uses them)
- Add adversarial codegen tests to lock the security contract
…) into extracted rules
- extractor: _cluster_to_rule adds promoted price features to entry_condition as {min: p10, max: p90}; all-NaN features are silently skipped
- codegen: _rule_to_context flattens optional entry_rsi14 / prior_5d_return bounds for the template
- template: SignalEngine._conditional_entry gates entry on real-time RSI and prior-5d-return when bounds present, falls back to time-window-only otherwise
Review follow-up (PR HKUDS#314, both verified against code):
- extractor: _heuristic_single_rule now forwards price_features so the single-cluster fallback carries the same RSI/return bounds (previously defaulted to ()), fixing silently behavior-only rules on small/homogeneous journals
- scanner: scan_today_signals honors the {min,max} condition format — added _compute_rsi + entry_rsi14 feature, mapped the rsi condition key, and taught _compare to range-check dict bounds; closes the scanner/backtest consistency gap
- tests: 28 total — extraction, codegen, template behaviour, plus 5 scanner cases (RSI in/out-of-band, prior-return dict band, insufficient history) and 1 single-cluster fallback case
Refs HKUDS#295
Signed-off-by: Robin1987China <41602358+Robin1987China@users.noreply.github.com>
8151f3a to
fbff848
Compare
|
Thanks for the careful read — both blocking issues were real, pushed fixes in Blocking 2 ( Blocking 1 ( One correction on the impact though: it was not "rule NEVER matches / zero matches". On finding #5 (tasks.md): that file lives in a local planning dir I keep out of the repo (same as the earlier autopilot PRs), so its absence here is expected, not a missing file. Non-blocking items — the unconditional RSI compute in the template, the hard-coded feature tuple in 3 spots, and |
|
Merged 🎉 Thanks @Robin1987China — carrying the RSI / prior-return bounds through into the generated |
Summary
Extracted rules now carry price-condition bounds when
entry_rsi14andprior_5d_returnare promoted into clustering (Step 1, #302). The generated SignalEngine uses these bounds for real conditional entry logic instead of blind hold-day replay.Why
Step 1 (#302) added price features to KMeans clustering but kept them silent in output rules. The generated SignalEngine still replays hold-day cadences blindly — enter every N bars. A rule like "enter when RSI is in 25-45" is far more actionable and backtestable. This PR closes the gap: promoted price features now flow through into rule
entry_conditiondicts, and the generated engine uses conditional entry logic.Changes
agent/src/shadow_account/extractor.py:_cluster_to_rulestores promoted price features inentry_conditionas{min: p10, max: p90}; all-NaN features silently skipped.agent/src/shadow_account/codegen.py:_rule_to_contextflattens optionalentry_rsi14/prior_5d_returnbounds for the template.agent/src/shadow_account/templates/signal_engine.py.j2:SignalEngine._conditional_entrygates entry on real-time RSI and prior-5d-return when bounds present, falls back to time-window-only otherwise.agent/tests/test_shadow_account.py: 16 new tests — extraction (5), codegen (4), template behavior (10), security injection (1).Test Plan
ruff checkextractor.py / codegen.py — 0 errors (template.j2F821 is pre-existing Jinja2)pytest agent/tests/test_shadow_account.py— 62 passedpytest --ignore=agent/tests/e2e_backtest --ignore=agent/tests/test_e2e_harness_v2.py— 4404 passed, 1 skippedopenspec validate --changes --strict— 4 items passedpytest agent/tests/test_shadow_codegen_security.py— 1 passedChecklist
Refs: #295