Skip to content

Quality+momentum production strategy pack through the four gates - #1

Merged
gupta-builds merged 1 commit into
mainfrom
feat/quality-momentum-strategy-pack
Jul 11, 2026
Merged

Quality+momentum production strategy pack through the four gates#1
gupta-builds merged 1 commit into
mainfrom
feat/quality-momentum-strategy-pack

Conversation

@gupta-builds

@gupta-builds gupta-builds commented Jul 10, 2026

Copy link
Copy Markdown
Owner

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 under tests/.

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).
  • Point-in-time fundamentals with a 90-day reporting lag; decisions at close i earn returns from i+1 — prefix-invariance test proves no lookahead.
  • Fail-closed eligibility: ETFs and equities with no computable quality sub-signal are INSUFFICIENT_DATA and never selectable; a cross-section of one holds cash at exactly 0.0. Nothing is imputed.
  • Costs via the existing gate cost model (5 bps/side); turnover is the two-sided sum of |Δweight| (entry from cash = 1.0). Every rebalance emits an audit record with formulas, inputs, and statement as-of dates.

Proof

  • Offline (CI): 464 tests green, 16 new. Includes a full four-gate pass at unchanged literature defaults on 1300 synthetic sessions, a thin-history fail-closed check, and a closed-loop test on the production hook_ref (citation → anant approval → gates → promotion → paper replay → journal lesson).
  • Live study (scripts/run_quality_momentum_study.py, no network): ran against data/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; spec quality_momentum_tilt_top3 stays 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.
  • Package invariants now cover research_data.strategies, the study script, and Docs/PHASE2_STRATEGY_PACK.md.
  • Stale hook_ref in test_brain_store.py now points at the real production hook.
  • Two flake fixes: UTC clock in the today-date model test (UTC+4 machines flaked after local midnight) and deadline=None on the duplicate-pk property test (repo convention).
  • Docs: 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 ACCUMULATE from the approved vocabulary), no fabrication paths, Kronos untouched, no new dependencies, universe unchanged at 14.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a quality-and-momentum investment strategy combining 12–1 momentum with company quality signals.
    • Added scheduled rebalancing, equal-weight top-three selection, benchmark comparisons, trading-cost tracking, and detailed audit records.
    • Added fail-closed safeguards for insufficient history, delayed fundamentals, limited eligibility, and excluded ETFs.
    • Added a manual study workflow with optional gate recording and paper-replay artifacts.
  • Documentation

    • Added strategy specifications, formulas, citations, operational guidance, and live-study workflow documentation.
    • Updated the module map and testing guidance.
  • Tests

    • Added offline, integration, no-lookahead, data-sufficiency, eligibility, and gate-validation coverage.

…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>
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds 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.

Changes

Quality Momentum Strategy

Layer / File(s) Summary
Strategy contract and rebalance engine
src/research_data/strategies/*
Adds the quality-momentum study, production hook, scoring formulas, eligibility rules, return accounting, turnover, and rebalance audit records.
Production registration and replay flow
scripts/run_quality_momentum_study.py, tests/test_closed_loop_production.py, tests/test_brain_store.py, tests/test_package_invariants.py
Adds CLI study execution, specification registration and approval, gate recording, optional promotion decisions, paper replay, and package canary coverage.
Synthetic data and behavioral tests
tests/synthetic.py, tests/test_strategy_quality_momentum.py, tests/test_models.py, tests/test_property_duplicate_pk.py
Adds fundamentals fixtures and tests for alignment, no-lookahead behavior, selection, scoring, turnover, gate outcomes, cross-section handling, and UTC/deadline stability.
Strategy pack documentation
Docs/PHASE2_STRATEGY_PACK.md, Docs/YEAR_AHEAD_BASE.md, Docs/fable5_run_memory.md
Documents formulas, operating constraints, study commands, module mapping, and recorded testing notes.

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main addition: a production quality+momentum strategy pack validated through gate checks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/quality-momentum-strategy-pack

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between f17bffb and f6f26c6.

📒 Files selected for processing (13)
  • Docs/PHASE2_STRATEGY_PACK.md
  • Docs/YEAR_AHEAD_BASE.md
  • Docs/fable5_run_memory.md
  • scripts/run_quality_momentum_study.py
  • src/research_data/strategies/__init__.py
  • src/research_data/strategies/quality_momentum.py
  • tests/synthetic.py
  • tests/test_brain_store.py
  • tests/test_closed_loop_production.py
  • tests/test_models.py
  • tests/test_package_invariants.py
  • tests/test_property_duplicate_pk.py
  • tests/test_strategy_quality_momentum.py

Comment on lines +13 to +15
```
hook_ref: research_data.strategies.quality_momentum:quality_momentum_tilt_hook
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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.

Suggested change
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

@gupta-builds
gupta-builds merged commit 5d598ee into main Jul 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant