Quality+momentum production strategy pack through the four gates - #1
Conversation
…tes. First strategy implementation in the installed package (not test-only): - src/research_data/strategies/quality_momentum.py — 50/50 composite of momentum 12-1 percentile and quality_fcf score, top-K equal-weight tilt, 21-session rebalance, 90-day fundamentals reporting lag, two-sided turnover costing via the existing gate cost model. Fail-closed eligibility: ETFs and no-sub-signal equities are INSUFFICIENT_DATA, a cross-section of one holds cash, nothing is imputed. - hook_ref research_data.strategies.quality_momentum:quality_momentum_tilt_hook resolves through the brain loop; RebalanceRecord audit trail carries formulas, inputs, and statement as-of dates for every decision. - Offline proof: tests/test_strategy_quality_momentum.py (alignment, prefix-invariance no-lookahead, eligibility rules, thin-history fail-closed, and a full four-gate pass at unchanged literature defaults on 1300 synthetic sessions) + tests/test_closed_loop_production.py (citation → anant approval → production hook → gates → promotion → paper replay → journal lesson). - scripts/run_quality_momentum_study.py — manual live study runner: no network, reads the existing DuckDB, records gate TestRunRecords and a paper replay journal artifact. First live run on 274 free-tier sessions: OOS gate failed closed as expected; spec stays not demo-eligible. - tests/synthetic.py gains a deterministic fundamentals generator; brain store test hook_ref now points at the real production hook; package invariants cover the strategies package and new docs/script. - Flake fixes: UTC clock in the today-date model test (UTC+4 machines), deadline=None on the duplicate-pk property test per repo convention. - Docs: PHASE2_STRATEGY_PACK.md, YEAR_AHEAD_BASE module map, run memory. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a production quality-momentum strategy with fail-closed selection, audit records, gate and paper-replay integration, synthetic fundamentals fixtures, comprehensive tests, a manual study script, and supporting documentation. ChangesQuality Momentum Strategy
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant StudyCLI
participant BrainStore
participant QualityMomentumHook
participant GateHarness
participant PaperEngine
StudyCLI->>BrainStore: register or reuse approved StrategySpec
StudyCLI->>QualityMomentumHook: run study against local DuckDB data
QualityMomentumHook-->>StudyCLI: strategy and benchmark returns
StudyCLI->>GateHarness: run and record gate batch
StudyCLI->>PaperEngine: replay approved study window
PaperEngine-->>StudyCLI: paper journal entries
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Docs/PHASE2_STRATEGY_PACK.md`:
- Line 38: Update the run_quality_momentum_study documentation to distinguish
dataset-level StrategyDataError warm-up failures from symbol-level
INSUFFICIENT_DATA rebalance skips, and use the implementation’s inclusive n <=
MIN_SESSIONS boundary instead of describing 253 usable sessions as skipped.
- Around line 13-15: Add explicit language identifiers to both fenced code
blocks in PHASE2_STRATEGY_PACK.md: mark the block containing hook_ref as yaml
and the formula block as text, including the corresponding fence around lines
21–31, to resolve markdownlint MD040.
In `@src/research_data/strategies/quality_momentum.py`:
- Line 166: Remove the unnecessary f-string prefix from the error message raised
in the rebalance interval validation, changing the literal in the relevant
strategy validation logic to a regular string while preserving the existing
message and StrategyDataError behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: af446bbb-28a5-4511-ae7e-26fdb7736cc2
📒 Files selected for processing (13)
Docs/PHASE2_STRATEGY_PACK.mdDocs/YEAR_AHEAD_BASE.mdDocs/fable5_run_memory.mdscripts/run_quality_momentum_study.pysrc/research_data/strategies/__init__.pysrc/research_data/strategies/quality_momentum.pytests/synthetic.pytests/test_brain_store.pytests/test_closed_loop_production.pytests/test_models.pytests/test_package_invariants.pytests/test_property_duplicate_pk.pytests/test_strategy_quality_momentum.py
| ``` | ||
| hook_ref: research_data.strategies.quality_momentum:quality_momentum_tilt_hook | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add language identifiers to fenced blocks.
markdownlint reports MD040 for both fences. Use yaml for the hook_ref block and text for the formula block.
Also applies to: 21-31
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 13-13: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Docs/PHASE2_STRATEGY_PACK.md` around lines 13 - 15, Add explicit language
identifiers to both fenced code blocks in PHASE2_STRATEGY_PACK.md: mark the
block containing hook_ref as yaml and the formula block as text, including the
corresponding fence around lines 21–31, to resolve markdownlint MD040.
Source: Linters/SAST tools
|
|
||
| ### Eligibility (fail-closed) | ||
|
|
||
| - < 253 usable sessions → momentum `INSUFFICIENT_DATA`, skipped that rebalance. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Document the dataset-level warm-up failure accurately.
run_quality_momentum_study raises StrategyDataError when the benchmark has n <= MIN_SESSIONS, before any rebalance can be skipped. Please distinguish this from symbol-level insufficient momentum data and match the inclusive boundary used by the implementation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Docs/PHASE2_STRATEGY_PACK.md` at line 38, Update the
run_quality_momentum_study documentation to distinguish dataset-level
StrategyDataError warm-up failures from symbol-level INSUFFICIENT_DATA rebalance
skips, and use the implementation’s inclusive n <= MIN_SESSIONS boundary instead
of describing 253 usable sessions as skipped.
| if top_k < 1: | ||
| raise StrategyDataError(f"top_k must be >= 1, got {top_k}") | ||
| if rebalance_every < 1: | ||
| raise StrategyDataError(f"rebalance_every_sessions must be >= 1") |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove extraneous f prefix — f-string has no placeholders.
Ruff F541 flags this as an error. If CI runs Ruff, this line will fail the build.
🔧 Fix
- raise StrategyDataError(f"rebalance_every_sessions must be >= 1")
+ raise StrategyDataError("rebalance_every_sessions must be >= 1")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| raise StrategyDataError(f"rebalance_every_sessions must be >= 1") | |
| raise StrategyDataError("rebalance_every_sessions must be >= 1") |
🧰 Tools
🪛 Ruff (0.15.20)
[error] 166-166: f-string without any placeholders
Remove extraneous f prefix
(F541)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/research_data/strategies/quality_momentum.py` at line 166, Remove the
unnecessary f-string prefix from the error message raised in the rebalance
interval validation, changing the literal in the relevant strategy validation
logic to a regular string while preserving the existing message and
StrategyDataError behavior.
Source: Linters/SAST tools
What this is
The first production strategy pack for the desk: a literature-default quality + momentum composite tilt that lives in the installed package, wired as a real spec
hook_ref, proven offline through all four gates, and runnable as a study against the live DuckDB file. Closes the gap where the only strategy hook lived undertests/.The strategy (
src/research_data/strategies/quality_momentum.py)composite = 0.5 × momentum-12-1 percentile + 0.5 × quality_fcf score; top-K (default 3) equal-weight, 21-session rebalance. Citations: Jegadeesh-Titman 1993, Novy-Marx 2013, AFP 2019 (in the module docstring and brain citations).INSUFFICIENT_DATAand never selectable; a cross-section of one holds cash at exactly 0.0. Nothing is imputed.Proof
hook_ref(citation →anantapproval → gates → promotion → paper replay → journal lesson).scripts/run_quality_momentum_study.py, no network): ran againstdata/market.duckdb(14 symbols × 274 sessions). Momentum warm-up leaves 21 sessions, so the OOS gate failed closed (train=14/oos=7 < 60) — recorded as a brain TestRunRecord; specquality_momentum_tilt_top3stays not demo-eligible. Strategy net +0.43% vs VOO +1.62% same window; replay journal exit NVDA −2.60% vs VOO +1.92%. Honest fail on thin free-tier history is the intended behavior.Also in this PR
tests/synthetic.py: deterministic quarterly fundamentals generator.research_data.strategies, the study script, andDocs/PHASE2_STRATEGY_PACK.md.hook_refintest_brain_store.pynow points at the real production hook.deadline=Noneon the duplicate-pk property test (repo convention).Docs/PHASE2_STRATEGY_PACK.md(formula, params, citations, free-tier limitation, how to run), module-map update, run-memory lessons.Guardrails checked: no execution language in new strings (only
ACCUMULATEfrom the approved vocabulary), no fabrication paths, Kronos untouched, no new dependencies, universe unchanged at 14.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests