From 5fade8eba15503e91e00ae34f6b29bf2f8fbcfe7 Mon Sep 17 00:00:00 2001 From: NeuZhou Date: Mon, 13 Apr 2026 05:12:30 +0000 Subject: [PATCH 1/2] fix(ci): export MomentumAdapter/ModelSelector/PredictionTracker/FinancialDataSplitter + align sandbox test exceptions - Add MomentumAdapter to stratevo.strategies.__init__ (was in combiner.py but not exported) - Add ModelSelector, PredictionTracker, FinancialDataSplitter to stratevo.ml.__init__ - Security sandbox tests: add ValueError to expected exceptions (sandbox raises ValueError for forbidden calls) --- stratevo/ml/__init__.py | 18 ++++++++++++++++++ stratevo/strategies/__init__.py | 3 ++- tests/test_security_audit.py | 4 ++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/stratevo/ml/__init__.py b/stratevo/ml/__init__.py index 12185b65..226bc1f5 100644 --- a/stratevo/ml/__init__.py +++ b/stratevo/ml/__init__.py @@ -55,3 +55,21 @@ __all__.append("WalkForwardPipeline") except ImportError: pass + +try: + from stratevo.ml.model_selection import ModelSelector + __all__.append("ModelSelector") +except ImportError: + pass + +try: + from stratevo.ml.prediction_tracker import PredictionTracker + __all__.append("PredictionTracker") +except ImportError: + pass + +try: + from stratevo.ml.data_splitter import FinancialDataSplitter + __all__.append("FinancialDataSplitter") +except ImportError: + pass diff --git a/stratevo/strategies/__init__.py b/stratevo/strategies/__init__.py index cc39bd6d..1bb736bf 100644 --- a/stratevo/strategies/__init__.py +++ b/stratevo/strategies/__init__.py @@ -1,11 +1,12 @@ """stratevo.strategies - Trading strategies.""" from stratevo.strategies.momentum_jt import MomentumJTStrategy -from stratevo.strategies.combiner import StrategyCombiner +from stratevo.strategies.combiner import StrategyCombiner, MomentumAdapter __all__ = [ "MomentumJTStrategy", "StrategyCombiner", + "MomentumAdapter", ] try: diff --git a/tests/test_security_audit.py b/tests/test_security_audit.py index 50e75437..77c98702 100644 --- a/tests/test_security_audit.py +++ b/tests/test_security_audit.py @@ -745,7 +745,7 @@ def test_factor_eval_cannot_import(self): volume = np.array([100.0, 200.0, 300.0]) # Attempting to import in a sandboxed eval should fail - with pytest.raises((NameError, TypeError)): + with pytest.raises((NameError, TypeError, ValueError)): evaluate_formula( "__import__('os').system('echo pwned')", close, high, low, volume, @@ -761,7 +761,7 @@ def test_factor_eval_cannot_access_builtins(self): low = np.array([0.5, 1.5, 2.5]) volume = np.array([100.0, 200.0, 300.0]) - with pytest.raises((NameError, TypeError, KeyError)): + with pytest.raises((NameError, TypeError, KeyError, ValueError)): evaluate_formula( "__builtins__['__import__']('os').listdir('.')", close, high, low, volume, From 1db5eea01ee17786c296bb732293621da2afeee2 Mon Sep 17 00:00:00 2001 From: NeuZhou Date: Mon, 13 Apr 2026 06:19:57 +0000 Subject: [PATCH 2/2] chore: improve contributor experience MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix CONTRIBUTING.md: finclaw → stratevo in clone URL and issue links - Fix SECURITY.md: update supported versions (add 6.x.x, 5.x.x security-only) - Scaffold: auto-place factor source in stratevo/factor_registry/builtin/ and tests in tests/ - Add CODEOWNERS for auto-assigned PR reviews - Add stale bot workflow (30 days stale, 14 days to close) - Add note about first-time fork contributor CI approval --- .github/CODEOWNERS | 18 ++++++++++++++ .github/workflows/stale.yml | 39 +++++++++++++++++++++++++++++++ CONTRIBUTING.md | 10 ++++---- SECURITY.md | 3 ++- stratevo/cli/commands/scaffold.py | 37 +++++++++++++++++++++++++---- 5 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/stale.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..5c8a4a8d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,18 @@ +# CODEOWNERS — auto-assign reviewers for PRs +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default: repo owner reviews everything +* @NeuZhou + +# Evolution engine — core, needs careful review +stratevo/evolution/ @NeuZhou +stratevo/fitness_plugins/ @NeuZhou + +# Factors — community contributions welcome +stratevo/factor_registry/builtin/ @NeuZhou + +# CI & Infrastructure +.github/ @NeuZhou + +# Trading — sensitive, needs extra scrutiny +stratevo/trading/ @NeuZhou diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..184ba95f --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,39 @@ +name: Stale + +on: + schedule: + - cron: '30 1 * * *' # daily at 01:30 UTC + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 14 + stale-issue-label: stale + stale-pr-label: stale + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed in 14 days if no further activity occurs. + If this is still relevant, please leave a comment or remove the stale label. + stale-pr-message: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed in 14 days if no further activity occurs. + If you're still working on this, please leave a comment or push a new commit. + close-issue-message: > + This issue was closed because it has been stale for 14 days with no activity. + Feel free to reopen if this is still relevant. + close-pr-message: > + This PR was closed because it has been stale for 14 days with no activity. + Feel free to reopen if you'd like to continue working on it. + exempt-issue-labels: 'pinned,priority,in-progress' + exempt-pr-labels: 'pinned,priority,in-progress' + exempt-all-milestones: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f006973..e035676c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,8 +12,8 @@ Thanks for your interest in contributing! StratEvo is an AI-native quantitative ```bash # 1. Clone the repo -git clone https://github.com/NeuZhou/finclaw.git -cd finclaw +git clone https://github.com/NeuZhou/stratevo.git +cd stratevo # 2. Install in development mode (with pre-commit hooks) make dev @@ -41,8 +41,8 @@ make check | Area | Description | |------|-------------| -| 🐛 Bug reports | Found something broken? [Open an issue](https://github.com/NeuZhou/finclaw/issues/new?template=bug_report.yml) | -| ✨ Features | Have an idea? [Request a feature](https://github.com/NeuZhou/finclaw/issues/new?template=feature_request.yml) | +| 🐛 Bug reports | Found something broken? [Open an issue](https://github.com/NeuZhou/stratevo/issues/new?template=bug_report.yml) | +| ✨ Features | Have an idea? [Request a feature](https://github.com/NeuZhou/stratevo/issues/new?template=feature_request.yml) | | 📈 Factors | Add a new alpha factor to the evolution engine | | 🏋️ Fitness Functions | Implement a new fitness function for strategy evaluation | | 📡 Data Sources | Connect a new data provider | @@ -164,6 +164,8 @@ All of the following must pass before a PR can be merged: - `ruff check .` — linting - No regressions in existing tests +> **Note for first-time contributors:** GitHub requires a maintainer to approve CI runs on your first PR from a fork. This is a security measure. Don't worry — we'll approve it quickly! + ## Issue Labels | Label | Description | diff --git a/SECURITY.md b/SECURITY.md index 88f18440..215d26dd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,8 @@ | Version | Supported | | ------- | ------------------ | -| 5.x.x | ✅ | +| 6.x.x | ✅ | +| 5.x.x | ✅ (security only) | | < 5.0 | ❌ | ## Reporting a Vulnerability diff --git a/stratevo/cli/commands/scaffold.py b/stratevo/cli/commands/scaffold.py index e9614b3c..673c1ef1 100644 --- a/stratevo/cli/commands/scaffold.py +++ b/stratevo/cli/commands/scaffold.py @@ -188,6 +188,19 @@ def test_supported_symbols(self): # ─── scaffold commands ────────────────────────────────────────────── +def _find_repo_root() -> Optional[str]: + """Walk up from CWD to find the repo root (contains stratevo/ package).""" + cur = os.path.abspath(".") + for _ in range(10): + if os.path.isdir(os.path.join(cur, "stratevo", "factor_registry")): + return cur + parent = os.path.dirname(cur) + if parent == cur: + break + cur = parent + return None + + def scaffold_factor( name: str, category: str = "custom", @@ -195,9 +208,24 @@ def scaffold_factor( ) -> tuple[str, str]: """Generate a factor file and its companion test. Returns tuple of paths.""" class_name = _to_class_name(name) - base = output_dir or "." - src_path = os.path.join(base, f"{name}_factor.py") - test_path = os.path.join(base, f"test_{name}_factor.py") + + # Auto-detect correct placement when output_dir is not given + if output_dir is None: + repo_root = _find_repo_root() + if repo_root is not None: + src_dir = os.path.join( + repo_root, "stratevo", "factor_registry", "builtin", + ) + test_dir = os.path.join(repo_root, "tests") + else: + src_dir = "." + test_dir = "." + else: + src_dir = output_dir + test_dir = output_dir + + src_path = os.path.join(src_dir, f"{name}_factor.py") + test_path = os.path.join(test_dir, f"test_{name}_factor.py") _write_file(src_path, _factor_template(name, category, class_name)) _write_file(test_path, _factor_test_template(name, category, class_name)) @@ -251,8 +279,7 @@ def cmd_scaffold(args: object) -> None: print(" Next steps:") print(f" 1. Open {src} and implement compute()") print(f" 2. Run tests: python -m pytest {test} -v") - print(" 3. Move into stratevo/factor_registry/builtin// when ready") - print(" 4. Submit a PR!") + print(" 3. Submit a PR!") elif scaffold_type == "fitness": name = args.name # type: ignore[attr-defined]