alphaengine 1.0.2__tar.gz → 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. alphaengine-1.0.3/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  2. alphaengine-1.0.3/.github/ISSUE_TEMPLATE/data_shape.md +24 -0
  3. alphaengine-1.0.3/.github/ISSUE_TEMPLATE/method_request.md +29 -0
  4. alphaengine-1.0.3/.github/dependabot.yml +13 -0
  5. alphaengine-1.0.3/.github/workflows/codeql.yml +29 -0
  6. {alphaengine-1.0.2 → alphaengine-1.0.3}/.github/workflows/publish.yml +3 -0
  7. alphaengine-1.0.3/CHANGELOG.md +49 -0
  8. alphaengine-1.0.3/CODE_OF_CONDUCT.md +49 -0
  9. alphaengine-1.0.3/CONTRIBUTING.md +39 -0
  10. {alphaengine-1.0.2 → alphaengine-1.0.3}/PKG-INFO +104 -36
  11. {alphaengine-1.0.2 → alphaengine-1.0.3}/README.md +102 -34
  12. {alphaengine-1.0.2 → alphaengine-1.0.3}/SECURITY.md +2 -2
  13. {alphaengine-1.0.2 → alphaengine-1.0.3}/pyproject.toml +1 -1
  14. {alphaengine-1.0.2 → alphaengine-1.0.3}/scripts/gen_docs.py +1 -1
  15. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/__init__.py +6 -1
  16. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/_version.py +25 -20
  17. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/bakeoff.py +74 -20
  18. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/brief.py +93 -30
  19. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/chart.py +53 -23
  20. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/sim.py +22 -4
  21. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/auth.py +56 -6
  22. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/cli.py +247 -22
  23. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/client/executor.py +12 -2
  24. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/client/session.py +104 -13
  25. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/commands.py +63 -5
  26. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/__init__.py +2 -0
  27. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/backtest.py +8 -3
  28. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/bakeoff.py +7 -3
  29. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/pairs.py +4 -1
  30. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/validation.py +83 -30
  31. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/demo.py +20 -6
  32. alphaengine-1.0.3/src/alphaengine/io.py +381 -0
  33. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/model.py +44 -1
  34. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/study/__init__.py +22 -2
  35. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/study/report.py +77 -10
  36. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/study/schema.py +84 -3
  37. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/sweep/runner.py +47 -6
  38. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/tui/app.py +33 -8
  39. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/tui/dispatch.py +89 -12
  40. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/tui/mark.py +2 -0
  41. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_bakeoff.py +4 -1
  42. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_cli.py +46 -3
  43. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_client.py +93 -4
  44. alphaengine-1.0.3/tests/test_coverage_paths.py +600 -0
  45. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_goldens.py +4 -1
  46. alphaengine-1.0.3/tests/test_io.py +116 -0
  47. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_math_audit.py +49 -4
  48. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_sweep.py +228 -0
  49. {alphaengine-1.0.2 → alphaengine-1.0.3}/.github/workflows/ci.yml +0 -0
  50. {alphaengine-1.0.2 → alphaengine-1.0.3}/.gitignore +0 -0
  51. {alphaengine-1.0.2 → alphaengine-1.0.3}/AGENTS.md +0 -0
  52. {alphaengine-1.0.2 → alphaengine-1.0.3}/LICENSE +0 -0
  53. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/assets/banner.png +0 -0
  54. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/assets/data_boundary.png +0 -0
  55. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/assets/session.png +0 -0
  56. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/figure_contract.md +0 -0
  57. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/workflows/evaluate_process.json +0 -0
  58. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/workflows/research_week.json +0 -0
  59. {alphaengine-1.0.2 → alphaengine-1.0.3}/docs/workflows/stress_study.json +0 -0
  60. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/__main__.py +0 -0
  61. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/__init__.py +0 -0
  62. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/answer.py +0 -0
  63. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/agent/driver.py +0 -0
  64. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/book.py +0 -0
  65. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/charts.py +0 -0
  66. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/client/__init__.py +0 -0
  67. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/client/agent.py +0 -0
  68. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/complete.py +0 -0
  69. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/connectors/__init__.py +0 -0
  70. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/context.py +0 -0
  71. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/active.py +0 -0
  72. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/allocate.py +0 -0
  73. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/covariance.py +0 -0
  74. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/cross_section.py +0 -0
  75. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/factors.py +0 -0
  76. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/market.py +0 -0
  77. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/options.py +0 -0
  78. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/panel.py +0 -0
  79. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/performance.py +0 -0
  80. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/portfolio.py +0 -0
  81. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/process.py +0 -0
  82. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/profile.py +0 -0
  83. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/rates.py +0 -0
  84. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/regime.py +0 -0
  85. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/relations.py +0 -0
  86. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/risk.py +0 -0
  87. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/screen.py +0 -0
  88. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/series_shapes.py +0 -0
  89. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/signals.py +0 -0
  90. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/stress.py +0 -0
  91. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/technical.py +0 -0
  92. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/trace.py +0 -0
  93. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/core/walkforward.py +0 -0
  94. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/demo_book.py +0 -0
  95. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/demo_returns.py +0 -0
  96. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/demo_signal.py +0 -0
  97. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/demo_universe.py +0 -0
  98. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/events.py +0 -0
  99. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/loaders.py +0 -0
  100. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/py.typed +0 -0
  101. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/repl.py +0 -0
  102. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/sweep/__init__.py +0 -0
  103. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/tui/__init__.py +0 -0
  104. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/tui/stage.py +0 -0
  105. {alphaengine-1.0.2 → alphaengine-1.0.3}/src/alphaengine/tui/typeface.py +0 -0
  106. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_agent.py +0 -0
  107. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_answer.py +0 -0
  108. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_book.py +0 -0
  109. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_commands.py +0 -0
  110. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_context.py +0 -0
  111. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_events.py +0 -0
  112. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_executor_ops.py +0 -0
  113. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_loaders.py +0 -0
  114. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_model.py +0 -0
  115. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_portfolio.py +0 -0
  116. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_process.py +0 -0
  117. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_quant_maths.py +0 -0
  118. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_relations.py +0 -0
  119. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_screen.py +0 -0
  120. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_signals.py +0 -0
  121. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_sim.py +0 -0
  122. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_smoke.py +0 -0
  123. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_trace.py +0 -0
  124. {alphaengine-1.0.2 → alphaengine-1.0.3}/tests/test_tui.py +0 -0
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a reproducible bug in AlphaEngine
4
+ title: "[Bug]: "
5
+ labels: ["bug"]
6
+ assignees: []
7
+ ---
8
+
9
+ ## Summary
10
+
11
+ A clear and concise description of what went wrong.
12
+
13
+ ## Reproduction
14
+
15
+ Minimal Python snippet or CLI command that reproduces the issue (using synthetic data or `alphaengine demo`):
16
+
17
+ ```python
18
+ import alphaengine
19
+ # ...
20
+ ```
21
+
22
+ ## Expected vs. actual behavior
23
+
24
+ - **Expected:**
25
+ - **Actual:**
26
+
27
+ ## Environment
28
+
29
+ - `alphaengine` version (`alphaengine version`):
30
+ - Python version:
31
+ - OS:
32
+ - `numpy` / `scipy` versions:
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Data shape / loader issue
3
+ about: Report a CSV, Parquet, or interop data-shape edge case
4
+ title: "[Data shape]: "
5
+ labels: ["data-shape"]
6
+ assignees: []
7
+ ---
8
+
9
+ ## Data source / format
10
+
11
+ Which loader or flag was used (`--data`, `load_panel`, `alphaengine.io.from_quantconnect`, `alphaengine.io.from_vectorbt`, etc.)?
12
+
13
+ ## Anonymized header and sample rows
14
+
15
+ Please paste 3–5 anonymized rows showing the header and column types (never paste proprietary price history):
16
+
17
+ ```csv
18
+ date,symbol,close
19
+ 2026-01-02,AAA,100.00
20
+ ```
21
+
22
+ ## Current behavior / error message
23
+
24
+ Paste the `DataShapeError` or unexpected behavior observed:
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Method request
3
+ about: Propose a published quantitative or statistical method for alphaengine.core
4
+ title: "[Method]: "
5
+ labels: ["enhancement", "methods"]
6
+ assignees: []
7
+ ---
8
+
9
+ ## Published reference
10
+
11
+ Everything in `alphaengine.core` comes from peer-reviewed literature. Please cite the paper or textbook:
12
+
13
+ - **Citation (authors, year, title, venue):**
14
+ - **DOI / URL:**
15
+
16
+ ## What question does it answer?
17
+
18
+ Describe the statistical or risk question this primitive answers and how it fits into the validation/research loop.
19
+
20
+ ## Proposed signature and inputs/outputs
21
+
22
+ ```python
23
+ def proposed_method(returns, ...):
24
+ ...
25
+ ```
26
+
27
+ ## Dependencies
28
+
29
+ Does this method run on `numpy` + `scipy` alone, or does it require an optional extra (e.g. `statsmodels`)?
@@ -0,0 +1,13 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 5
8
+
9
+ - package-ecosystem: "github-actions"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+ open-pull-requests-limit: 5
@@ -0,0 +1,29 @@
1
+ name: codeql
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "20 6 * * 1"
10
+
11
+ permissions:
12
+ contents: read
13
+ security-events: write
14
+
15
+ jobs:
16
+ analyze:
17
+ name: Analyze (python)
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - name: Checkout repository
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Initialize CodeQL
24
+ uses: github/codeql-action/init@v3
25
+ with:
26
+ languages: python
27
+
28
+ - name: Perform CodeQL Analysis
29
+ uses: github/codeql-action/analyze@v3
@@ -33,6 +33,9 @@ jobs:
33
33
  - uses: actions/setup-python@v5
34
34
  with:
35
35
  python-version: "3.12"
36
+ - name: Verify git tag matches __version__
37
+ run: |
38
+ python -c "import os, runpy, sys; v = runpy.run_path('src/alphaengine/_version.py')['__version__']; ref = os.environ.get('GITHUB_REF_NAME') or os.environ.get('GITHUB_REF', '').removeprefix('refs/tags/'); exp = f'v{v}'; sys.exit(f'Tag {ref!r} != {exp!r}' if ref != exp else 0)"
36
39
  - run: python -m pip install --upgrade build
37
40
  - run: python -m build
38
41
  - uses: actions/upload-artifact@v4
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+
3
+ All notable changes to `alphaengine` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to Semantic Versioning (with computed golden values
7
+ treated as part of the public contract).
8
+
9
+ ## [1.0.3] - 2026-10-24
10
+
11
+ ### Added
12
+ - **Sealed studies (Schema 1.2):** Canonical-JSON SHA-256 content hash (`content_sha256` with sorted keys and fixed 8-decimal float formatting), full 64-hex `data_hash`, and optional per-trial ledger export (`SweepResult.ledger()`, `include_ledger=True`).
13
+ - **CLI study verbs:** `alphaengine seal <study.json>` (writes `content_sha256` and verifies on re-run) and `alphaengine report <study.json> [--retail]` (reports to `/api/me/runs/ingest` by default or `/api/retail/ledger/import` with `--retail`).
14
+ - **Complete study ingest payload:** `study/report.py` `_payload()` now includes `selection` (PBO), `psr_vs_zero`, `sr0_expected_max`, `min_track_record_length`, `grid_keys`, `n_failed_trials`, `content_sha256`, and optional `assignment_id` and `thesis_id`.
15
+ - **Interop loaders (`alphaengine.io`):** `from_quantconnect(path)` and `from_vectorbt(portfolio_or_csv)` construct a `Study` with returns, performance metrics, data hash, and trial-count provenance (`"asserted"` when provided/present, `"not_recorded"` with verdict refusal when absent).
16
+ - **Credentials purge:** `alphaengine logout --purge` removes `~/.config/alphaengine/credentials.json`, cleans up its config directory, and clears managed keys from the current process.
17
+ - **API URL override:** `ALPHAENGINE_API_URL` environment variable and unified `DEFAULT_API_URL` constant across CLI and study reporting.
18
+ - **Security & governance:** Weekly Dependabot configuration (pip + GitHub Actions), Python CodeQL workflow, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, and GitHub issue templates.
19
+
20
+ ### Changed
21
+ - **Opt-in series sharing on `/traces`:** `_report_trace` sends scalar figures only by default. Uploading full series to `/api/harness/runs/{id}/traces` requires `--share-series` or `ALPHAENGINE_SHARE_SERIES=1`; any payload sent without opt-in is guarded at most 512 elements.
22
+ - **Deliberate math & golden updates:**
23
+ - `deflated_sharpe(..., n_trials=1)` no longer silently deflates as `N=2`. At `n_trials=1`, `sr0_expected_max` is `0.0` (was `0.037` on the `rets(9, 200)` golden) and `deflated_sharpe` equals `psr_vs_zero` (`0.8434`, was `0.6875` on the golden), with an explicit note that no multiple-testing deflation applies.
24
+ - `SweepResult.verdict()` passes the cross-trial Sharpe dispersion across valid grid trials into `deflated_sharpe` per Bailey & López de Prado (2014), falling back to single-series sampling error only when fewer than 2 valid trials exist.
25
+ - Renamed `cpcv_score` to `combinatorial_oos_stability` (keeping `cpcv_score` as a deprecated alias) to accurately describe single-stream purged/embargoed sub-sample stability.
26
+ - `pbo_cscv` validates `n_splits` (even integer $\ge 2$) and raises a clear `ValueError`.
27
+ - **Neutral framing:** Added *"Research tool, not investment advice"* disclaimer to README, `--help`, and the boot screen; renamed user-facing bakeoff `survivor` / `survivor_weights` to `top_ranked_config` / `model_weights` while preserving backward-compatible dictionary keys.
28
+ - **Release guard:** `.github/workflows/publish.yml` verifies that the git tag matches `v{__version__}` before building.
29
+
30
+ ## [1.0.2] - 2026-10-20
31
+
32
+ ### Added
33
+ - **Four-pass adversarial GAN bakeoff:** `run_bakeoff` and `core.bakeoff.referee` run four passes (eight candidate proposals across `equal`, `hrp`, `risk_parity`, `inverse_vol`, `var_sorted`, `low_vol`, `momentum`, and `reversion` against eight regime, tail, rate, and crash-cluster attacks) under a hard 12-call, 60-second ceiling.
34
+ - **Multi-pass bakeoff scorecard in PDF reports:** Cumulative wealth across passes, in-sample vs. post-attack Sharpe comparison, and candidate scorecard table.
35
+
36
+ ## [1.0.1] - 2026-10-19
37
+
38
+ ### Added
39
+ - **Harness simulation tools:** Added `monte_carlo` and `scenarios` to the local session tool stack.
40
+ - **Multi-page PDF & text reports:** Accepted session traces render multi-panel portfolio, scenario stress, Monte Carlo fan, and bakeoff reports to the user's `Downloads` folder.
41
+
42
+ ### Fixed
43
+ - **Ledoit-Wolf sample-count divisor:** Fixed sample covariance scaling in `ledoit_wolf_cov` so `hrp_weights` and `risk_parity_weights` differentiate weights across wide panels.
44
+
45
+ ## [1.0.0] - 2026-10-18
46
+
47
+ ### Added
48
+ - **Interactive terminal simulator (`alphaengine`):** Full-screen Textual session where plain-English questions execute as a deterministic stack of local tool calls (`profile`, `align`, `regime`, `price`, `reprice`, `correlation`, `covariance`, `cointegration`, `portfolio`, `overlap`, `performance`, `var`, `process`, `workflow`, `bakeoff`) with citation guarding.
49
+ - **Pinned cash rate & statistical refinements:** Default risk-free rate pinned to the H.15 3-month Treasury constant maturity (`4.21%` as of `2026-10-06`, `DGS3MO`; pass `0` for excess of zero), automatic Newey-West HAC lag selection in factor regressions, and Benjamini-Hochberg FDR survivors in cointegration screening.
@@ -0,0 +1,49 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement
40
+
41
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
42
+ reported to the project maintainers at **wfe818@gmail.com**. All complaints will
43
+ be reviewed and investigated promptly and fairly.
44
+
45
+ ## Attribution
46
+
47
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
48
+ version 2.1, available at
49
+ <https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
@@ -0,0 +1,39 @@
1
+ # Contributing to AlphaEngine
2
+
3
+ Thank you for your interest in contributing to AlphaEngine.
4
+
5
+ ## Core design constraints
6
+
7
+ Before opening a pull request, please keep these architectural rules in mind:
8
+
9
+ 1. **Two runtime dependencies (`numpy` and `scipy`).** Optional extras (`factors`, `agents`, `connectors`, `tui`) are lazy-imported so `import alphaengine` never requires network clients, LLM SDKs, or UI frameworks.
10
+ 2. **Goldens are a public contract.** Computed values in `tests/test_goldens.py` are pinned so saved studies remain reproducible. Never modify a golden value simply to make a test pass; deliberate mathematical corrections require a version bump and a `CHANGELOG.md` entry explaining what changed and why.
11
+ 3. **Data boundary.** Computation runs locally. Without explicit opt-in (`--share-series` or `ALPHAENGINE_SHARE_SERIES=1`), no list longer than 512 elements may leave the machine.
12
+ 4. **Trial-count honesty.** Trial counts must be derived from the executed grid (`derived_from_grid`), explicitly marked (`asserted`), or `not_recorded` (which refuses an `edge` verdict).
13
+
14
+ ## Local development setup
15
+
16
+ ```bash
17
+ python -m venv .venv
18
+ source .venv/bin/activate # Windows PowerShell: .\.venv\Scripts\Activate.ps1
19
+ pip install -e ".[dev,factors]"
20
+ ```
21
+
22
+ ## Required checks before submitting
23
+
24
+ Run all five CI gates locally before pushing:
25
+
26
+ ```bash
27
+ python -m ruff check src tests
28
+ python -m ruff format --check src tests
29
+ python -m mypy src
30
+ python -m pytest
31
+ python -m pytest tests -m golden -q
32
+ python scripts/gen_docs.py --check
33
+ ```
34
+
35
+ If you add or modify CLI commands or flags in `src/alphaengine/commands.py`, regenerate the README command table with:
36
+
37
+ ```bash
38
+ python scripts/gen_docs.py --write
39
+ ```
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: alphaengine
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: Validated research tooling for investment strategies: deflation, overfitting detection, and honest trial counts.
5
- Project-URL: Homepage, https://github.com/quantOSC/alphaengine
5
+ Project-URL: Homepage, https://qosai.app
6
6
  Project-URL: Documentation, https://github.com/quantOSC/alphaengine#readme
7
7
  Project-URL: Source, https://github.com/quantOSC/alphaengine
8
8
  Project-URL: Issues, https://github.com/quantOSC/alphaengine/issues
@@ -53,8 +53,10 @@ Description-Content-Type: text/markdown
53
53
 
54
54
  <p align="center">
55
55
  <strong>The research loop, on your machine.</strong><br>
56
- Ask what is worth looking at, whether it holds up, how much to hold,<br>
57
- and whether anything has crossed a line. Your data never leaves.
56
+ Ask what is worth looking at, whether it holds up, what sizing the risk model implies,<br>
57
+ and whether anything has crossed a line.<br>
58
+ Computation runs locally; when you are signed in to quantOS, summary figures are reported to your account; full series are shared only if you opt in.<br>
59
+ <em>Research tool, not investment advice.</em>
58
60
  </p>
59
61
 
60
62
  <p align="center">
@@ -84,7 +86,7 @@ python -m alphaengine
84
86
  ┌──────────┬─────────────────────────────────────┬──────────────────┐
85
87
  │ ● maths │ Ask in plain English. │ sharpe │
86
88
  │ always │ │ mean / std √252 │
87
- │ ○ flows │ > which names are overbought? │ ▁▂▃▅▇█▇▅▃ │
89
+ │ ○ flows │ > which names rank highest on RSI? │ ▁▂▃▅▇█▇▅▃ │
88
90
  │ ○ ask │ │ 0 0.0012 │
89
91
  │ │ chose screen_universe │ 1 -0.0040 │
90
92
  │ loaded │ │ │
@@ -103,8 +105,19 @@ alphaengine
103
105
  > screen
104
106
  ```
105
107
 
106
- Prices stay on this machine. A math line's series can travel on its own trace
107
- so it can be charted. The figures posted with the step stay summaries.
108
+ Computation runs locally on this machine. When you are signed in to quantOS,
109
+ summary figures are reported to your account; full series are shared on
110
+ `/traces` only if you opt in (`--share-series` or `ALPHAENGINE_SHARE_SERIES=1`).
111
+
112
+ ---
113
+
114
+ ## Where this sits
115
+
116
+ AlphaEngine computes locally on your machine against your own data.
117
+ [quantOS](https://qosai.app) is the hosted research workspace that keeps the
118
+ record and writes committee reports. Everything in AlphaEngine's math and sweep
119
+ layer runs offline with no account; signing in connects local runs and sealed
120
+ studies to your quantOS workspace.
108
121
 
109
122
  ---
110
123
 
@@ -121,7 +134,7 @@ IS the question; only two of them need code of yours.
121
134
  | Is this real, once you count the tries? | `validate` | `validate_study` | prices + your backtest |
122
135
  | Where does it break? | `stress` | `stress_study` | a return series |
123
136
  | Is it new, or my book again? | `overlap` | `check_overlap` | the candidate + the book |
124
- | How much should I hold? | `size` | `size_position` | a return series |
137
+ | What sizing does the risk model imply? | `size` | `size_position` | a return series |
125
138
  | Still inside the lines? | `monitor` | `monitor_sleeve` | a return series |
126
139
 
127
140
  ```bash
@@ -141,7 +154,7 @@ Or say it in plain English and let your own model pick:
141
154
 
142
155
  ```bash
143
156
  alphaengine
144
- ❯ which of my names are overbought on RSI?
157
+ ❯ which of my names rank highest on RSI?
145
158
  ```
146
159
 
147
160
  That second path is EXPLORATORY: the model chooses a workflow and then chooses
@@ -157,12 +170,28 @@ Each is useful without the one above it. `login` lights the next one.
157
170
 
158
171
  | Rung | What you need | What you get |
159
172
  |---|---|---|
160
- | **The maths** | nothing | Every statistic, offline, forever. No account. |
161
- | **Workflows** | a QuantOS `ae_live_` key (`login`) | The loop end to end, with the record. |
173
+ | **The maths** | nothing | `alphaengine demo`, `alphaengine process`, and the full Python API (`import alphaengine`, `sweep`, `core`), offline, forever. No account. |
174
+ | **Workflows** | a portal-issued `ae_live_` key (`login` or `QUANTOS_API_KEY`; request access at [https://qosai.app/access](https://qosai.app/access)) | The eight workflow commands (`diagnose`, `screen`, `signal`, `validate`, `stress`, `overlap`, `size`, `monitor`) plus `workflows`, `runs`, `gaps`, `tonight`, and `run <workflow>`. |
162
175
  | **Ask anything** | your OWN model key (`login anthropic`) | Plain English in. Runs under your account, not ours. |
163
176
 
164
- Nothing here stores a model key: it is read from your environment at call time
165
- and handed to the provider's own client. There is no field to put one in.
177
+ The eight workflow commands (`diagnose`, `screen`, `signal`, `validate`,
178
+ `stress`, `overlap`, `size`, `monitor`) and `workflows` / `runs` require a
179
+ portal-issued `ae_live_` key because the workflow graph and account record live
180
+ on the server (request access at [https://qosai.app/access](https://qosai.app/access)).
181
+ `alphaengine demo`, `alphaengine process`, and the Python library (`import alphaengine`)
182
+ work completely offline without an account or key.
183
+
184
+ **How credentials are handled.** QuantOS never receives your LLM provider key:
185
+ it goes directly from this process to your chosen provider. By default, keys can
186
+ be supplied via environment variables (`QUANTOS_API_KEY`, `ANTHROPIC_API_KEY`,
187
+ `OPENAI_API_KEY`, etc.), which touches nothing on disk. If you run `login` and
188
+ say **yes** to *"Stay signed in on this machine?"*, `auth.py` writes an optional
189
+ `~/.config/alphaengine/credentials.json` (`0600` permissions;
190
+ `%APPDATA%\alphaengine\credentials.json` on Windows) so you do not have to
191
+ re-paste in every terminal. Environment variables always win over that file.
192
+ To avoid disk storage entirely, set environment variables only (or answer `n`
193
+ at `login`). To delete the stored file at any time, run
194
+ `alphaengine logout --purge` (or `alphaengine logout`).
166
195
 
167
196
  ```mermaid
168
197
  flowchart LR
@@ -220,6 +249,15 @@ distinction is the whole of the data boundary below.
220
249
 
221
250
  ---
222
251
 
252
+ ## What's new in 1.0.3
253
+
254
+ - **Opt-in series sharing & 512-element wire guard:** By default, workflow step traces (`/api/harness/runs/{id}/traces`) report scalar figures only; full series are shared only when you pass `--share-series` or set `ALPHAENGINE_SHARE_SERIES=1`.
255
+ - **Sealed studies (schema 1.2) & CLI reporting:** Studies now carry a canonical-JSON SHA-256 seal (`content_sha256`), a full 64-hex `data_hash`, and an optional per-trial ledger (`include_ledger=True`). Use `alphaengine seal <study.json>` to seal or verify a study file, and `alphaengine report <study.json> [--retail]` to ingest into quantOS or the retail experiment ledger.
256
+ - **Math fixes:** `SweepResult.verdict()` passes cross-trial Sharpe dispersion into `deflated_sharpe` per Bailey & López de Prado (2014); `deflated_sharpe(n_trials=1)` returns `psr_vs_zero` with `sr0_expected_max = 0.0` instead of deflating as `N=2`; `combinatorial_oos_stability` replaces `cpcv_score` (kept as a deprecated alias); and `pbo_cscv` validates that `n_splits` is an even integer $\ge 2$.
257
+ - **Interop loaders (`alphaengine.io`):** `from_quantconnect(path)` and `from_vectorbt(portfolio_or_csv)` build a `Study` with returns and preserve trial-count provenance (`"asserted"` vs. `"not_recorded"`).
258
+
259
+ See [CHANGELOG.md](CHANGELOG.md) for 1.0.0–1.0.3 release notes.
260
+
223
261
  ## What's new in 0.9.0
224
262
 
225
263
  `next_open` fills wait for a prior close. The first bar of a next-open backtest
@@ -227,9 +265,10 @@ no longer trades that bar's own signal at that bar's open. Close fills are
227
265
  unchanged, and the published goldens do not move.
228
266
 
229
267
  The session is the full-screen terminal (`pip install "alphaengine[tui]"`).
230
- Each statistic keeps its formula, inputs, and series. Those series post to
231
- `/api/harness/runs/{id}/traces`. A portal that rejects the trace does not fail
232
- the run.
268
+ Each statistic keeps its formula, inputs, and series locally. By default,
269
+ `/api/harness/runs/{id}/traces` receives scalar figures only; full series are
270
+ uploaded only when you opt in (`--share-series` or `ALPHAENGINE_SHARE_SERIES=1`).
271
+ A portal that rejects the trace does not fail the run.
233
272
 
234
273
  ## What's new in 0.8.0
235
274
 
@@ -246,8 +285,8 @@ are a public contract from this release. The study schema is **1.1**: optional
246
285
  | Figures that already travel | `{kind, key, title}` hints a portal maps to a chart | `charts`, [figure contract](docs/figure_contract.md) |
247
286
 
248
287
  The terminal boot is the README banner: tracked `ALPHAENGINE`, the subtitle,
249
- a teal-left / amber-right mesh (plateau, valley, knife-edge ridge), and
250
- `your data never leaves.` `alphaengine process` fits a DGP offline, no account.
288
+ a teal-left / amber-right mesh (plateau, valley, knife-edge ridge), and the
289
+ local-computation notice. `alphaengine process` fits a DGP offline, no account.
251
290
 
252
291
  ```python
253
292
  from alphaengine.core import ou_calibrate, garch_calibrate, dgp_stress, grinold_alpha
@@ -323,9 +362,11 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
323
362
  | `validate` | is this real? | shell + session |
324
363
  | `stress` | where does it break? | shell + session |
325
364
  | `overlap` | is it new, or my book again? | shell + session |
326
- | `size` | how much? | shell + session |
365
+ | `size` | what sizing does the risk model imply? | shell + session |
327
366
  | `monitor` | still inside the lines? | shell + session |
328
367
  | `run <workflow>` | run one workflow exactly as written | shell + session |
368
+ | `seal <study.json>` | write canonical content_sha256 into a study and verify on re-run | shell |
369
+ | `report <study.json> [--retail]` | send a saved study to quantOS (/api/me/runs/ingest or --retail ledger) | shell |
329
370
  | `<anything else>` | ask in plain English; your model picks a workflow and drives it | session |
330
371
  | `status` | the current run, and what is loaded | session |
331
372
 
@@ -348,7 +389,7 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
348
389
 
349
390
  | Command | Does | Where |
350
391
  |---|---|---|
351
- | `logout` | remove stored credentials from this machine | shell + session |
392
+ | `logout [--purge]` | remove stored credentials from this machine | shell + session |
352
393
  | `version` | print the version | shell |
353
394
  | `help` | the short list: demo, login, load, then a question | session |
354
395
  | `quit` | leave the session | session |
@@ -376,8 +417,11 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
376
417
  | `--input K=V` | a workflow input; repeatable |
377
418
  | `--quiet` | only the result, no step narration |
378
419
  | `--stream` | print the answer once the citation guard has passed |
420
+ | `--share-series` | opt in to uploading full series on run traces (default off; or $ALPHAENGINE_SHARE_SERIES=1) |
379
421
  | `--limit N` | how many rows to show (default 25) |
380
422
  | `--budget N` | how many runs a night is worth (default 3) |
423
+ | `--purge` | remove ~/.config/alphaengine/credentials.json and clean up its config directory |
424
+ | `--retail` | send the study to the retail experiment ledger (/api/retail/ledger/import) |
381
425
 
382
426
  ### Examples
383
427
 
@@ -404,10 +448,14 @@ alphaengine commands
404
448
  alphaengine run screen_universe --universe sp500
405
449
  alphaengine run size_position --data returns.csv
406
450
  alphaengine run validate_study --project research.momentum
451
+ alphaengine seal study.json
452
+ alphaengine report study.json
453
+ alphaengine report study.json --retail
407
454
  alphaengine models
408
455
  alphaengine trace
409
456
  alphaengine run screen_universe --thesis momentum
410
457
  alphaengine logout
458
+ alphaengine logout --purge
411
459
  alphaengine version
412
460
  ```
413
461
 
@@ -461,7 +509,7 @@ def backtest_fn(*, data, fast, slow):
461
509
  ```
462
510
 
463
511
  `data` is whatever you want it to be — a DataFrame, a dict of series, an array.
464
- The package never inspects it and it never leaves your machine.
512
+ The package never inspects it; computation runs locally on your machine.
465
513
 
466
514
  ## What it does
467
515
 
@@ -531,8 +579,10 @@ Study.from_sweep(r, label="momentum, 9 configs").report()
531
579
 
532
580
  What crosses is an explicit allowlist: the trial count and how it was obtained,
533
581
  a content hash of the data, the verdict, the shape of the neighbourhood, the
534
- performance figures. Your returns, your prices and your parameter grid stay on
535
- the machine, and a guard keyed on length rather than field name refuses to send
582
+ performance figures. Computation runs locally; when you are signed in to
583
+ quantOS, summary figures are reported to your account, and full series are
584
+ shared only if you opt in (`--share-series` or `ALPHAENGINE_SHARE_SERIES=1`).
585
+ Without opt-in, a guard keyed on length (at most 512 elements) refuses to send
536
586
  anything series-shaped whatever it is called.
537
587
 
538
588
  Reporting is the only part of this package that touches a network, so it is the
@@ -541,15 +591,16 @@ makes no network call.
541
591
 
542
592
  ## Where this sits in QuantOS
543
593
 
544
- AlphaEngine is the open research layer of the [QuantOS](https://github.com/quantOSC)
545
- platform. It is the piece that runs on your machine, against your data, and it
546
- is complete on its own: everything above works offline and forever, at no cost.
594
+ AlphaEngine is the open research layer of [quantOS](https://qosai.app)
595
+ ([GitHub](https://github.com/quantOSC)). AlphaEngine computes locally on your
596
+ machine, against your data, and is complete on its own: everything above works
597
+ offline and forever, at no cost.
547
598
 
548
- The QuantOS platform builds on it. Studies produced here can be persisted to a
549
- firm's record, referenced when an idea becomes a position, and assembled into
550
- the reports that go to an investment committee or an allocator. The library
551
- computes; the platform remembers and reports. The two halves are separated so
552
- that the part touching your data has no reason to phone home.
599
+ quantOS ([https://qosai.app](https://qosai.app)) is the hosted research workspace
600
+ that keeps the record and writes committee reports. Studies produced here can be
601
+ persisted to a firm's record, referenced when an idea becomes a position, and
602
+ assembled into the reports that go to an investment committee or an allocator.
603
+ The library computes; the platform remembers and reports.
553
604
 
554
605
  ## The methods
555
606
 
@@ -563,19 +614,35 @@ Frontier." *Journal of Risk* 15(2), 3 to 44.
563
614
  Bailey, D. H., and López de Prado, M. (2014). "The Deflated Sharpe Ratio:
564
615
  Correcting for Selection Bias, Backtest Overfitting, and Non-Normality."
565
616
  *Journal of Portfolio Management* 40(5), 94 to 107.
617
+ *Implementation notes:* Grid trials are treated as independent (`N = len(grid)`
618
+ with no effective-$N$ clustering adjustment). Cross-trial Sharpe dispersion
619
+ ($\sigma_{\text{SR}}$) is computed across valid grid trials when at least two
620
+ exist, falling back to single-series Lo (2002) sampling error otherwise. At
621
+ `n_trials=1`, no multiple-testing deflation applies (`sr0_expected_max = 0.0`,
622
+ `deflated_sharpe = psr_vs_zero`). Verdict thresholds on `deflated_sharpe`:
623
+ `< 0.5` is `likely_noise`, `0.5` to `< 0.9` is `marginal`, and `DSR >= 0.9` is
624
+ `robust`.
566
625
 
567
626
  **Probability of Backtest Overfitting via CSCV**
568
627
  Bailey, D. H., Borwein, J., López de Prado, M., and Zhu, Q. J. (2017). "The
569
628
  Probability of Backtest Overfitting." *Journal of Computational Finance* 20(4),
570
629
  39 to 69.
571
630
 
572
- **Combinatorial purged cross-validation**
631
+ **Combinatorial out-of-sample stability (`combinatorial_oos_stability`)**
573
632
  López de Prado, M. (2018). *Advances in Financial Machine Learning.* Wiley,
574
633
  chapters 7 and 12.
634
+ *Implementation note:* Evaluates a single pre-computed return stream across all
635
+ $\binom{G}{k}$ held-out block combinations with `purge` and `embargo` trimmed
636
+ from each test block (no model re-fitting or stitched training predictions).
637
+ `cpcv_score` is retained as a deprecated alias.
575
638
 
576
639
  **Hierarchical Risk Parity**
577
640
  López de Prado, M. (2016). "Building Diversified Portfolios that Outperform
578
641
  Out of Sample." *Journal of Portfolio Management* 42(4), 59 to 69.
642
+ *Implementation note:* `hrp_weights` uses the correlation metric distance
643
+ $d_{ij} = \sqrt{\frac{1}{2}(1 - \rho_{ij})}$ with single-linkage hierarchical
644
+ clustering (`scipy.cluster.hierarchy.linkage(..., method="single")`), followed
645
+ by quasi-diagonalization and recursive inverse-variance bisection.
579
646
 
580
647
  **Covariance shrinkage and spectral denoising**
581
648
  Ledoit, O., and Wolf, M. (2004). "A Well-Conditioned Estimator for
@@ -690,10 +757,11 @@ controls switched off. Only a run that could not execute a step exits non-zero.
690
757
  > CLI, is the one thing that cannot work here. You cannot have isolation and
691
758
  > in-process data access, and your data not moving is the point.
692
759
 
693
- **No LLM dependency and no key field.** `AgentDriver` takes a callable, so you
694
- bring your own model by passing a function. There is nowhere in this tool to put
695
- a model key, ours or yours, and that is how "runs under your own account" is
696
- satisfied structurally rather than promised.
760
+ **No LLM dependency in the core.** `AgentDriver` takes a callable, so you bring
761
+ your own model by passing a function or setting your provider's environment
762
+ variable. LLM keys are never sent to QuantOS; if you choose to persist keys
763
+ locally at `login`, they live in `~/.config/alphaengine/credentials.json`
764
+ (mode `0600`) and are removed by `alphaengine logout --purge`.
697
765
 
698
766
  ## Development
699
767