alphaengine 1.1.0__tar.gz → 1.1.2__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 (145) hide show
  1. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/workflows/publish.yml +1 -1
  2. {alphaengine-1.1.0 → alphaengine-1.1.2}/CHANGELOG.md +29 -0
  3. {alphaengine-1.1.0 → alphaengine-1.1.2}/PKG-INFO +61 -27
  4. {alphaengine-1.1.0 → alphaengine-1.1.2}/README.md +60 -26
  5. {alphaengine-1.1.0 → alphaengine-1.1.2}/SECURITY.md +4 -5
  6. alphaengine-1.1.2/docs/free-vs-firm.md +88 -0
  7. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/project-format.md +4 -2
  8. {alphaengine-1.1.0 → alphaengine-1.1.2}/pyproject.toml +1 -4
  9. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/__init__.py +20 -3
  10. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/_version.py +4 -2
  11. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/sim.py +7 -4
  12. alphaengine-1.1.2/src/alphaengine/auth.py +294 -0
  13. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/cli.py +98 -79
  14. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/client/executor.py +1 -0
  15. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/commands.py +4 -11
  16. alphaengine-1.1.2/src/alphaengine/contracts/__init__.py +11 -0
  17. alphaengine-1.1.2/src/alphaengine/contracts/hash_golden.json +113 -0
  18. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/__init__.py +1 -1
  19. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/allocate.py +8 -1
  20. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/backtest.py +2 -2
  21. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/pairs.py +4 -2
  22. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/performance.py +13 -8
  23. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/process.py +8 -1
  24. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/risk.py +8 -10
  25. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/technical.py +3 -1
  26. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/validation.py +29 -11
  27. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/envelope.py +5 -2
  28. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/ideas.py +3 -7
  29. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/io.py +2 -1
  30. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/ledger.py +84 -10
  31. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/memo.py +12 -3
  32. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/pipeline.py +6 -0
  33. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/project.py +26 -27
  34. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/study/__init__.py +4 -0
  35. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/study/report.py +12 -52
  36. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/study/schema.py +75 -10
  37. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/sweep/runner.py +30 -36
  38. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/sync.py +19 -14
  39. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/tui/dispatch.py +2 -3
  40. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/tui/stage.py +2 -1
  41. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/watch.py +3 -2
  42. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_cli.py +16 -11
  43. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_coverage_paths.py +1 -7
  44. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_goldens.py +16 -0
  45. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_math_audit.py +81 -0
  46. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_project.py +216 -21
  47. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_sim.py +2 -2
  48. alphaengine-1.1.2/tests/test_smoke.py +180 -0
  49. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_sweep.py +8 -34
  50. alphaengine-1.1.0/src/alphaengine/auth.py +0 -203
  51. alphaengine-1.1.0/tests/test_smoke.py +0 -96
  52. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  53. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/ISSUE_TEMPLATE/data_shape.md +0 -0
  54. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/ISSUE_TEMPLATE/method_request.md +0 -0
  55. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/dependabot.yml +0 -0
  56. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/workflows/ci.yml +0 -0
  57. {alphaengine-1.1.0 → alphaengine-1.1.2}/.github/workflows/codeql.yml +0 -0
  58. {alphaengine-1.1.0 → alphaengine-1.1.2}/.gitignore +0 -0
  59. {alphaengine-1.1.0 → alphaengine-1.1.2}/AGENTS.md +0 -0
  60. {alphaengine-1.1.0 → alphaengine-1.1.2}/CODE_OF_CONDUCT.md +0 -0
  61. {alphaengine-1.1.0 → alphaengine-1.1.2}/CONTRIBUTING.md +0 -0
  62. {alphaengine-1.1.0 → alphaengine-1.1.2}/LICENSE +0 -0
  63. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/assets/banner.png +0 -0
  64. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/assets/data_boundary.png +0 -0
  65. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/assets/session.png +0 -0
  66. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/figure_contract.md +0 -0
  67. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/plugins.md +0 -0
  68. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/result-format.md +0 -0
  69. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/workflows/evaluate_process.json +0 -0
  70. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/workflows/research_week.json +0 -0
  71. {alphaengine-1.1.0 → alphaengine-1.1.2}/docs/workflows/stress_study.json +0 -0
  72. {alphaengine-1.1.0 → alphaengine-1.1.2}/examples/process_stress_monitor.yaml +0 -0
  73. {alphaengine-1.1.0 → alphaengine-1.1.2}/examples/screen_validate_size.yaml +0 -0
  74. {alphaengine-1.1.0 → alphaengine-1.1.2}/scripts/gen_docs.py +0 -0
  75. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/__main__.py +0 -0
  76. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/__init__.py +0 -0
  77. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/answer.py +0 -0
  78. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/bakeoff.py +0 -0
  79. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/brief.py +0 -0
  80. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/chart.py +0 -0
  81. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/agent/driver.py +0 -0
  82. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/book.py +0 -0
  83. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/charts.py +0 -0
  84. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/client/__init__.py +0 -0
  85. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/client/agent.py +0 -0
  86. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/client/session.py +0 -0
  87. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/complete.py +0 -0
  88. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/connectors/__init__.py +0 -0
  89. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/context.py +0 -0
  90. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/active.py +0 -0
  91. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/bakeoff.py +0 -0
  92. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/covariance.py +0 -0
  93. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/cross_section.py +0 -0
  94. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/factors.py +0 -0
  95. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/market.py +0 -0
  96. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/options.py +0 -0
  97. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/panel.py +0 -0
  98. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/portfolio.py +0 -0
  99. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/profile.py +0 -0
  100. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/rates.py +0 -0
  101. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/regime.py +0 -0
  102. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/relations.py +0 -0
  103. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/screen.py +0 -0
  104. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/series_shapes.py +0 -0
  105. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/signals.py +0 -0
  106. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/stress.py +0 -0
  107. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/trace.py +0 -0
  108. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/core/walkforward.py +0 -0
  109. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/datasets.py +0 -0
  110. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/demo.py +0 -0
  111. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/demo_book.py +0 -0
  112. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/demo_returns.py +0 -0
  113. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/demo_signal.py +0 -0
  114. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/demo_universe.py +0 -0
  115. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/events.py +0 -0
  116. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/loaders.py +0 -0
  117. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/model.py +0 -0
  118. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/plugins.py +0 -0
  119. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/py.typed +0 -0
  120. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/repl.py +0 -0
  121. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/sweep/__init__.py +0 -0
  122. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/tui/__init__.py +0 -0
  123. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/tui/app.py +0 -0
  124. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/tui/mark.py +0 -0
  125. {alphaengine-1.1.0 → alphaengine-1.1.2}/src/alphaengine/tui/typeface.py +0 -0
  126. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_agent.py +0 -0
  127. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_answer.py +0 -0
  128. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_bakeoff.py +0 -0
  129. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_book.py +0 -0
  130. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_client.py +0 -0
  131. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_commands.py +0 -0
  132. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_context.py +0 -0
  133. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_events.py +0 -0
  134. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_executor_ops.py +0 -0
  135. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_io.py +0 -0
  136. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_loaders.py +0 -0
  137. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_model.py +0 -0
  138. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_portfolio.py +0 -0
  139. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_process.py +0 -0
  140. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_quant_maths.py +0 -0
  141. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_relations.py +0 -0
  142. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_screen.py +0 -0
  143. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_signals.py +0 -0
  144. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_trace.py +0 -0
  145. {alphaengine-1.1.0 → alphaengine-1.1.2}/tests/test_tui.py +0 -0
@@ -35,7 +35,7 @@ jobs:
35
35
  python-version: "3.12"
36
36
  - name: Verify git tag matches __version__
37
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)"
38
+ python -c "import os, tomllib, sys; v = tomllib.load(open('pyproject.toml', 'rb'))['project']['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)"
39
39
  - run: python -m pip install --upgrade build
40
40
  - run: python -m build
41
41
  - uses: actions/upload-artifact@v4
@@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to Semantic Versioning (with computed golden values
7
7
  treated as part of the public contract).
8
8
 
9
+ ## [1.1.2] - 2026-10-24
10
+
11
+ ### Fixed
12
+ - **Array-like inputs across validation and metrics (`core/validation.py`, `core/performance.py`, `core/risk.py`):** Replaced truthiness checks (`returns or []`, `if equity_curve:`, `if benchmark_returns:`, `portfolio_returns or []`) with `np.asarray` conversion so `deflated_sharpe`, `min_track_record_length`, `combinatorial_oos_stability` / `cpcv_score`, `pbo_cscv`, `performance_report`, and `compute_var_cvar` accept Python `list`, NumPy `ndarray`, and pandas `Series` / `DataFrame` without raising `ValueError: The truth value of an array/Series is ambiguous`.
13
+ - **Complete `_GATED_COMMANDS` and unified 401/403 refusal (`cli.py`):** Added `workflows`, `runs`, `gaps`, and `tonight` to `_GATED_COMMANDS` so every gated command goes through `require_firm_key()`, and replaced the separate `"Not authenticated"` refusal block with `FIRM_KEY_ERROR_MESSAGE`.
14
+ - **Live-monitor trial accounting (`watch.py`):** `ae monitor <run> --live <file>` (`monitor_live`) now records `n_trials=0` (`already_counted_in_ledger=True`) so out-of-sample live drift checks never inflate the project's cumulative trial count `N`.
15
+ - **Clean SLSQP bounds clipping (`core/process.py`, `core/allocate.py`):** Suppressed the benign SciPy SLSQP bounds-clipping `RuntimeWarning` during GARCH(1,1) and risk-parity optimization.
16
+
17
+ ### Changed
18
+ - **Security policy (`SECURITY.md`):** Replaced personal email contact with [GitHub Private Vulnerability Reporting / Security Advisories](https://github.com/quantOSC/alphaengine/security/advisories/new).
19
+
20
+ ## [1.1.1] - 2026-10-24
21
+
22
+ ### Added
23
+ - **Single firm-key choke point (`alphaengine.auth.require_firm_key`):** Used by every gated command (`run`, `show`, `monitor`/`watch`, `validate`, `screen`, `diagnose`, `signal`, `stress`, `overlap`, `size`, `workflows`, `runs`, `gaps`, `tonight`, and the CLI OS: `init`, `log`, `diff`, `rerun`, `data`, `idea`, `seal`, `report`, `remote`, `push`, `pull`, `plugin`, `shell`). Free and keyless forever: `import alphaengine`, `alphaengine demo`, `alphaengine process`, and `ae verify`.
24
+ - **OS keychain support & key redaction:** `auth.py` stores keys in the OS keychain (`keyring`) when available with `~/.config/alphaengine/credentials.json` (`0600`) as fallback, and `redact_secrets()` strips `ae_live_*` tokens from CLI output, error messages, tracebacks, and `--json` envelopes.
25
+ - **Canonical SHA-256 contract & golden fixture (`alphaengine/contracts/hash_golden.json`):** Unified `content_sha256`, study seals, `.alphaengine/objects/` CAS blobs, dataset hashes, and parameter hashes on `canonical_sha256()` (Schema 1.2 sorted-key JSON, `.8f` float formatting, 64-hex SHA-256). Added `verify_study_mode()` so studies sealed under the pre-1.2 legacy format still verify and are labeled `"legacy"`.
26
+ - **Free vs. Firm documentation (`docs/free-vs-firm.md`):** Documents the offline open-source library boundary, firm `ae_live_` Connect keys, and what uses the network.
27
+ - **Socket-blocked offline smoke test:** Replaced the import-only check in `tests/test_smoke.py` with a socket-blocked test (`socket.socket` and `socket.create_connection` patched to raise) that imports `alphaengine` and executes `deflated_sharpe` (DSR), `pbo_cscv` (PBO via CSCV), `combinatorial_oos_stability`, `performance_report`, `compute_var_cvar`, `sweep`, `load_csv`, `from_quantconnect`, and `from_vectorbt`.
28
+
29
+ ### Changed
30
+ - **Cumulative project-wide trials & DSR everywhere:** Project-wide cumulative trial counts (`project_trials` across `.alphaengine/ledger/trials.jsonl`) now feed every DSR — run output, headline `deflated_sharpe` and `n_trials`, `ae push` payloads, and `ae seal` studies — with no per-run or per-family reset.
31
+ - **Identical re-run deduplication:** Re-running an identical configuration (`command`, `params_hash`, and `dataset_hashes` matching a prior run in `.alphaengine/ledger/runs.jsonl`) records `duplicate_of` and adds `0` new trials (`9 -> 9`), whereas distinct configurations always increment cumulative `N` (`9 -> 18`).
32
+ - **Removed bare `except Exception: pass` in `sweep/runner.py`:** `_maybe_record_project_sweep` now raises a descriptive `RuntimeError` when a discovered `.alphaengine/` project ledger cannot be written.
33
+ - **Metadata-driven `__version__` and naming cleanup:** `__version__` is loaded via `importlib.metadata.version("alphaengine")` with no hardcoded version strings; standardized `combinatorial_oos_stability` (with `cpcv_score` emitting `DeprecationWarning`) and `PBO via CSCV` across docs and top-level exports.
34
+
35
+ ### Removed
36
+ - **Retail CLI path (`--retail` and `/api/retail/ledger/import`):** Removed `--retail` from `report` and CLI dispatch. Retail ledger import is performed via web upload in the quantOS portal.
37
+
9
38
  ## [1.1.0] - 2026-10-24
10
39
 
11
40
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: alphaengine
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Validated research tooling for investment strategies: deflation, overfitting detection, and honest trial counts.
5
5
  Project-URL: Homepage, https://qosai.app
6
6
  Project-URL: Documentation, https://github.com/quantOSC/alphaengine#readme
@@ -170,16 +170,15 @@ Each is useful without the one above it. `login` lights the next one.
170
170
 
171
171
  | Rung | What you need | What you get |
172
172
  |---|---|---|
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>`. |
173
+ | **The maths** | nothing | `alphaengine demo`, `alphaengine process`, `ae verify`, and the full Python API (`import alphaengine`, `sweep`, `core`), offline, forever. No account. |
174
+ | **Firm workflows & CLI OS** | a firm `ae_live_` key from **Connect** in your firm's quantOS portal (`login` or `QUANTOS_API_KEY`; see [docs/free-vs-firm.md](docs/free-vs-firm.md)) | The eight workflow commands (`diagnose`, `screen`, `signal`, `validate`, `stress`, `overlap`, `size`, `monitor`), `workflows`, `runs`, `gaps`, `tonight`, `run <workflow>`, and the Research OS (`init`, `log`, `show`, `diff`, `rerun`, `watch`, `data`, `idea`, `seal`, `report`, `remote`, `push`, `pull`, `plugin`, `shell`). |
175
175
  | **Ask anything** | your OWN model key (`login anthropic`) | Plain English in. Runs under your account, not ours. |
176
176
 
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.
177
+ The workflow commands and Research OS require a firm `ae_live_` key issued through
178
+ **Connect** in your firm's quantOS portal (see [docs/free-vs-firm.md](docs/free-vs-firm.md)).
179
+ There is no retail CLI key. `alphaengine demo`, `alphaengine process`, `ae verify`,
180
+ and the Python library (`import alphaengine`) work completely offline without an
181
+ account or key.
183
182
 
184
183
  **How credentials are handled.** QuantOS never receives your LLM provider key:
185
184
  it goes directly from this process to your chosen provider. By default, keys can
@@ -197,7 +196,7 @@ at `login`). To delete the stored file at any time, run
197
196
  flowchart LR
198
197
  A["demo / import<br/>offline maths"] --> B["login<br/>QuantOS key"]
199
198
  B --> C["login anthropic<br/>your model"]
200
- A -.->|"no account"| D["DSR · PBO · ICIR · HRP"]
199
+ A -.->|"no account"| D["DSR · PBO (CSCV) · ICIR · HRP"]
201
200
  B -.->|"ae_live_"| E["screen · validate · runs"]
202
201
  C -.->|"BYOK"| F["plain English in"]
203
202
  ```
@@ -249,11 +248,35 @@ distinction is the whole of the data boundary below.
249
248
 
250
249
  ---
251
250
 
252
- ## Research OS quickstart
251
+ ## Quickstart: Offline First, Then Connect for Firms
253
252
 
254
- AlphaEngine works like `git` for quantitative research: initialize a local `.alphaengine/` project once, and every dataset registration, hypothesis, workflow run, parameter sweep, and sealed study is recorded in an append-only content-addressed ledger on your disk (see [project format](docs/project-format.md), [result envelope](docs/result-format.md), and [plugins](docs/plugins.md)). Both `alphaengine` and the short alias `ae` run the same entry point.
253
+ ### 1. Free and offline (no account, no API key)
254
+
255
+ The Python library (`import alphaengine`), every validation and portfolio math primitive, `alphaengine demo`, `alphaengine process`, and `ae verify` run 100% offline with no key (see [Free vs. Firm](docs/free-vs-firm.md)):
256
+
257
+ ```bash
258
+ pip install alphaengine
259
+
260
+ # Run the built-in 9-trial parameter sweep and inspect the surface & DSR verdict
261
+ alphaengine demo
262
+
263
+ # Fit OU, GBM, jump-diffusion, and GARCH(1,1) processes and run DGP stress offline
264
+ alphaengine process
265
+
266
+ # Verify any sealed Study artifact (Schema 1.2 or legacy) without an account
267
+ ae verify study.json
268
+ ```
269
+
270
+ ### 2. Connect for firms (`ae_live_` key from the quantOS portal)
271
+
272
+ CLI workflows and the local-first Research OS (`ae init`, project ledgers, dataset registry, hypothesis tracking, sealing, memos, and workspace sync) require a firm `ae_live_` key issued through **Connect** in your firm's [quantOS portal](https://qosai.app). There is no retail CLI key or `--retail` flag.
273
+
274
+ Both `alphaengine` and the short alias `ae` run the same entry point (see [Free vs. Firm](docs/free-vs-firm.md), [project format](docs/project-format.md), [result envelope](docs/result-format.md), and [plugins](docs/plugins.md)):
255
275
 
256
276
  ```bash
277
+ # Sign in with your firm's Connect key (stored in OS keychain or 0600 fallback)
278
+ ae login
279
+
257
280
  # 1. Initialize a local .alphaengine/ workspace
258
281
  ae init --name equity-momentum
259
282
 
@@ -274,7 +297,7 @@ ae log --family mom_20d
274
297
  ae show HEAD
275
298
  ae rerun HEAD
276
299
 
277
- # 6. Seal into a Schema 1.2 Study, verify against data, and export a committee memo
300
+ # 6. Seal into a Schema 1.2 Study, verify against data (keyless), and export a committee memo
278
301
  ae seal HEAD
279
302
  ae verify .alphaengine/studies/HEAD.json
280
303
  ae report memo HEAD --format md
@@ -282,6 +305,18 @@ ae report memo HEAD --format md
282
305
 
283
306
  ---
284
307
 
308
+ ## What's new in 1.1.1
309
+
310
+ - **Single firm-key choke point (`require_firm_key()`):** Every gated CLI workflow (`run`, `validate`, `screen`, `diagnose`, `signal`, `stress`, `overlap`, `size`, `monitor`) and Research OS command (`init`, `log`, `show`, `diff`, `rerun`, `watch`, `data`, `idea`, `seal`, `report`, `remote`, `push`, `pull`, `plugin`, `shell`) validates a firm `ae_live_` key upfront via `alphaengine.auth.require_firm_key()`. The library, `alphaengine demo`, `alphaengine process`, and `ae verify` remain free and keyless.
311
+ - **OS keychain storage & secret redaction:** `auth.py` stores credentials in the OS keychain (`keyring`) when available with `~/.config/alphaengine/credentials.json` (`0600`) as fallback, and automatically redacts `ae_live_*` tokens from CLI output, errors, tracebacks, and `--json` envelopes.
312
+ - **Retail CLI path removed:** Removed `--retail` and `/api/retail/ledger/import` from the CLI; retail ledger import happens via web upload in the quantOS portal.
313
+ - **Unified canonical SHA-256 contract & golden fixture:** `canonical_sha256()` (Schema 1.2 sorted-key JSON with fixed `.8f` float formatting and full 64-hex SHA-256) powers `content_sha256`, study seals, CAS objects, dataset hashes, and parameter hashes. Legacy seals still verify and are labeled `"legacy"`. Shipped `alphaengine/contracts/hash_golden.json` in the wheel for cross-repo verification.
314
+ - **Project-wide cumulative trials & DSR everywhere (with re-run deduplication):** The project-wide cumulative trial ledger feeds every DSR — run output, headline `deflated_sharpe` and `n_trials`, push payloads, and sealed studies — with no per-run or per-family reset. Re-running an identical configuration (`command` + `params_hash` + `dataset_hashes`) records `duplicate_of` and adds `0` new trials (`9 -> 9`), while distinct configurations increment cumulative `N` (`9 -> 18`).
315
+ - **Metadata versioning & socket-blocked offline test:** `__version__` is read from `importlib.metadata.version("alphaengine")` with no hardcoded version strings, `combinatorial_oos_stability` and `PBO via CSCV` naming is standardized throughout, and `tests/test_smoke.py` enforces zero socket calls across DSR, PBO via CSCV, `combinatorial_oos_stability`, metrics, `sweep`, and loaders.
316
+ - **Free vs. Firm guide:** Added [`docs/free-vs-firm.md`](docs/free-vs-firm.md).
317
+
318
+ ---
319
+
285
320
  ## What's new in 1.1.0
286
321
 
287
322
  - **Local-first project workspace (`ae init`):** Creates `.alphaengine/` (`project.toml`, `ledger/`, `data/`, `studies/`, `ideas/`, `universes/`, and SHA-256 content-addressed `objects/`). Commands walk up from the current directory to find the project; outside a project, every command behaves as a standalone call.
@@ -295,7 +330,7 @@ ae report memo HEAD --format md
295
330
  ## What's new in 1.0.3
296
331
 
297
332
  - **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`.
298
- - **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.
333
+ - **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>` to ingest into quantOS.
299
334
  - **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$.
300
335
  - **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"`).
301
336
 
@@ -316,7 +351,7 @@ A portal that rejects the trace does not fail the run.
316
351
  ## What's new in 0.8.0
317
352
 
318
353
  Named processes, Grinold, and a frozen chart contract. Existing goldens
319
- (deflated Sharpe, PBO, performance, screen) are byte-identical. New figures
354
+ (deflated Sharpe, PBO via CSCV, performance, screen) are byte-identical. New figures
320
355
  are a public contract from this release. The study schema is **1.1**: optional
321
356
  `process` and `charts` fields; 1.0 studies still load.
322
357
 
@@ -345,7 +380,7 @@ a = grinold_alpha(panel, ic=0.05, vols=vols) # alpha stays here; missing vols a
345
380
  ## What's new in 0.7.0
346
381
 
347
382
  The daily modelling morning, and the overnight book, without a third
348
- dependency. Existing goldens (deflated Sharpe, PBO, performance, screen) are
383
+ dependency. Existing goldens (deflated Sharpe, PBO via CSCV, performance, screen) are
349
384
  byte-identical. New figures are a public contract from this release.
350
385
 
351
386
  | You have | You get | Module |
@@ -416,7 +451,7 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
416
451
  | `watch <pipeline.yaml> [--interval SECONDS]` | re-run a declarative pipeline whenever a registered dataset file changes | shell + session |
417
452
  | `seal <study.json \| run \| idea>` | write canonical content_sha256 into a study (or seal a project run/idea) | shell + session |
418
453
  | `verify <study.json> [--data FILE]` | verify a study.json content_sha256 seal and optionally re-check figures | shell + session |
419
- | `report <study.json> [--retail] \| memo <idea \| run> [--format md \| pdf]` | send a study to quantOS, or export a cited committee memo (md/pdf) | shell + session |
454
+ | `report <study.json> \| memo <idea \| run> [--format md \| pdf]` | send a study to quantOS (/api/me/runs/ingest), or export a cited committee memo | shell + session |
420
455
  | `<anything else>` | ask in plain English; your model picks a workflow and drives it | session |
421
456
  | `status` | the current run, and what is loaded | session |
422
457
 
@@ -477,7 +512,6 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
477
512
  | `--limit N` | how many rows to show (default 25) |
478
513
  | `--budget N` | how many runs a night is worth (default 3) |
479
514
  | `--purge` | remove ~/.config/alphaengine/credentials.json and clean up its config directory |
480
- | `--retail` | send the study to the retail experiment ledger (/api/retail/ledger/import) |
481
515
  | `--name NAME` | name for the initialized project or registered dataset |
482
516
  | `--idea ID` | attach the run or filter the log to a local hypothesis id |
483
517
  | `--family FAMILY` | strategy or experiment family for cumulative trial counting |
@@ -532,7 +566,6 @@ alphaengine seal HEAD
532
566
  alphaengine verify study.json
533
567
  alphaengine verify study.json --data prices.csv
534
568
  alphaengine report study.json
535
- alphaengine report study.json --retail
536
569
  alphaengine report memo HEAD --format md
537
570
  alphaengine models
538
571
  alphaengine trace
@@ -587,7 +620,7 @@ above exists to copy:
587
620
  per-period returns themselves. `sweep` does `np.asarray(list(raw))`, so a dict
588
621
  of results iterates its *keys* and fails on the first string.
589
622
 
590
- **Return the same length for every combination.** PBO splits the trial matrix
623
+ **Return the same length for every combination.** PBO via CSCV splits the trial matrix
591
624
  into time blocks and compares configurations within each block, which only means
592
625
  anything if they line up in time. Ragged output is refused rather than truncated,
593
626
  because silently trimming produces a confident number over series that do not
@@ -639,7 +672,7 @@ diffable, and versioned so it still parses in two years.
639
672
 
640
673
  | Module | Contents |
641
674
  |---|---|
642
- | `alphaengine.core` | deflated Sharpe, PSR, PBO via CSCV, CPCV, minimum track record length, performance and risk |
675
+ | `alphaengine.core` | deflated Sharpe, PSR, PBO via CSCV, `combinatorial_oos_stability`, minimum track record length, performance and risk |
643
676
  | `alphaengine.core.panel` | cross-sectional rank, z-score, winsorize, neutralize |
644
677
  | `alphaengine.core.signals` | IC, ICIR, quantile returns, decay |
645
678
  | `alphaengine.core.cross_section` | Fama-MacBeth, quantile book with turnover |
@@ -678,14 +711,15 @@ Study.from_sweep(r, label="momentum, 9 configs").report()
678
711
  What crosses is an explicit allowlist: the trial count and how it was obtained,
679
712
  a content hash of the data, the verdict, the shape of the neighbourhood, the
680
713
  performance figures. Computation runs locally; when you are signed in to
681
- quantOS, summary figures are reported to your account, and full series are
682
- shared only if you opt in (`--share-series` or `ALPHAENGINE_SHARE_SERIES=1`).
683
- Without opt-in, a guard keyed on length (at most 512 elements) refuses to send
714
+ quantOS, summary figures are reported to your account, and full return series
715
+ upload only with `--share-series` (or `ALPHAENGINE_SHARE_SERIES=1`). Without
716
+ `--share-series`, a guard keyed on length (at most 512 elements) refuses to send
684
717
  anything series-shaped whatever it is called.
685
718
 
686
- Reporting is the only part of this package that touches a network, so it is the
687
- only part that is not imported until you call it. `import alphaengine` still
688
- makes no network call.
719
+ ### What uses the network
720
+
721
+ - **Zero network calls (completely offline):** `import alphaengine`, `sweep()`, `Study.save()`, `Study.load()`, every math primitive in `alphaengine.core` (`deflated_sharpe`, `pbo_cscv`, `combinatorial_oos_stability`, performance, risk, covariance, allocation, process calibration/stress, Grinold), local loaders (`load_csv`, `from_quantconnect`, `from_vectorbt`), `alphaengine demo`, `alphaengine process`, and `ae verify`.
722
+ - **Uses the network (requires a firm `ae_live_` key from Connect):** `login` / `whoami`, CLI workflows (`validate`, `screen`, `diagnose`, `decompose`, `pairs`, `walkforward`, `compare`, `size`, `run`, `workflows`, `runs`, `gaps`, `tonight`), `Study.report()` / `ae report`, and workspace sync (`ae seal` key check, `ae push`, `ae pull`). Full return series upload **only** when `--share-series` is passed.
689
723
 
690
724
  ## Where this sits in QuantOS
691
725
 
@@ -123,16 +123,15 @@ Each is useful without the one above it. `login` lights the next one.
123
123
 
124
124
  | Rung | What you need | What you get |
125
125
  |---|---|---|
126
- | **The maths** | nothing | `alphaengine demo`, `alphaengine process`, and the full Python API (`import alphaengine`, `sweep`, `core`), offline, forever. No account. |
127
- | **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>`. |
126
+ | **The maths** | nothing | `alphaengine demo`, `alphaengine process`, `ae verify`, and the full Python API (`import alphaengine`, `sweep`, `core`), offline, forever. No account. |
127
+ | **Firm workflows & CLI OS** | a firm `ae_live_` key from **Connect** in your firm's quantOS portal (`login` or `QUANTOS_API_KEY`; see [docs/free-vs-firm.md](docs/free-vs-firm.md)) | The eight workflow commands (`diagnose`, `screen`, `signal`, `validate`, `stress`, `overlap`, `size`, `monitor`), `workflows`, `runs`, `gaps`, `tonight`, `run <workflow>`, and the Research OS (`init`, `log`, `show`, `diff`, `rerun`, `watch`, `data`, `idea`, `seal`, `report`, `remote`, `push`, `pull`, `plugin`, `shell`). |
128
128
  | **Ask anything** | your OWN model key (`login anthropic`) | Plain English in. Runs under your account, not ours. |
129
129
 
130
- The eight workflow commands (`diagnose`, `screen`, `signal`, `validate`,
131
- `stress`, `overlap`, `size`, `monitor`) and `workflows` / `runs` require a
132
- portal-issued `ae_live_` key because the workflow graph and account record live
133
- on the server (request access at [https://qosai.app/access](https://qosai.app/access)).
134
- `alphaengine demo`, `alphaengine process`, and the Python library (`import alphaengine`)
135
- work completely offline without an account or key.
130
+ The workflow commands and Research OS require a firm `ae_live_` key issued through
131
+ **Connect** in your firm's quantOS portal (see [docs/free-vs-firm.md](docs/free-vs-firm.md)).
132
+ There is no retail CLI key. `alphaengine demo`, `alphaengine process`, `ae verify`,
133
+ and the Python library (`import alphaengine`) work completely offline without an
134
+ account or key.
136
135
 
137
136
  **How credentials are handled.** QuantOS never receives your LLM provider key:
138
137
  it goes directly from this process to your chosen provider. By default, keys can
@@ -150,7 +149,7 @@ at `login`). To delete the stored file at any time, run
150
149
  flowchart LR
151
150
  A["demo / import<br/>offline maths"] --> B["login<br/>QuantOS key"]
152
151
  B --> C["login anthropic<br/>your model"]
153
- A -.->|"no account"| D["DSR · PBO · ICIR · HRP"]
152
+ A -.->|"no account"| D["DSR · PBO (CSCV) · ICIR · HRP"]
154
153
  B -.->|"ae_live_"| E["screen · validate · runs"]
155
154
  C -.->|"BYOK"| F["plain English in"]
156
155
  ```
@@ -202,11 +201,35 @@ distinction is the whole of the data boundary below.
202
201
 
203
202
  ---
204
203
 
205
- ## Research OS quickstart
204
+ ## Quickstart: Offline First, Then Connect for Firms
206
205
 
207
- AlphaEngine works like `git` for quantitative research: initialize a local `.alphaengine/` project once, and every dataset registration, hypothesis, workflow run, parameter sweep, and sealed study is recorded in an append-only content-addressed ledger on your disk (see [project format](docs/project-format.md), [result envelope](docs/result-format.md), and [plugins](docs/plugins.md)). Both `alphaengine` and the short alias `ae` run the same entry point.
206
+ ### 1. Free and offline (no account, no API key)
207
+
208
+ The Python library (`import alphaengine`), every validation and portfolio math primitive, `alphaengine demo`, `alphaengine process`, and `ae verify` run 100% offline with no key (see [Free vs. Firm](docs/free-vs-firm.md)):
209
+
210
+ ```bash
211
+ pip install alphaengine
212
+
213
+ # Run the built-in 9-trial parameter sweep and inspect the surface & DSR verdict
214
+ alphaengine demo
215
+
216
+ # Fit OU, GBM, jump-diffusion, and GARCH(1,1) processes and run DGP stress offline
217
+ alphaengine process
218
+
219
+ # Verify any sealed Study artifact (Schema 1.2 or legacy) without an account
220
+ ae verify study.json
221
+ ```
222
+
223
+ ### 2. Connect for firms (`ae_live_` key from the quantOS portal)
224
+
225
+ CLI workflows and the local-first Research OS (`ae init`, project ledgers, dataset registry, hypothesis tracking, sealing, memos, and workspace sync) require a firm `ae_live_` key issued through **Connect** in your firm's [quantOS portal](https://qosai.app). There is no retail CLI key or `--retail` flag.
226
+
227
+ Both `alphaengine` and the short alias `ae` run the same entry point (see [Free vs. Firm](docs/free-vs-firm.md), [project format](docs/project-format.md), [result envelope](docs/result-format.md), and [plugins](docs/plugins.md)):
208
228
 
209
229
  ```bash
230
+ # Sign in with your firm's Connect key (stored in OS keychain or 0600 fallback)
231
+ ae login
232
+
210
233
  # 1. Initialize a local .alphaengine/ workspace
211
234
  ae init --name equity-momentum
212
235
 
@@ -227,7 +250,7 @@ ae log --family mom_20d
227
250
  ae show HEAD
228
251
  ae rerun HEAD
229
252
 
230
- # 6. Seal into a Schema 1.2 Study, verify against data, and export a committee memo
253
+ # 6. Seal into a Schema 1.2 Study, verify against data (keyless), and export a committee memo
231
254
  ae seal HEAD
232
255
  ae verify .alphaengine/studies/HEAD.json
233
256
  ae report memo HEAD --format md
@@ -235,6 +258,18 @@ ae report memo HEAD --format md
235
258
 
236
259
  ---
237
260
 
261
+ ## What's new in 1.1.1
262
+
263
+ - **Single firm-key choke point (`require_firm_key()`):** Every gated CLI workflow (`run`, `validate`, `screen`, `diagnose`, `signal`, `stress`, `overlap`, `size`, `monitor`) and Research OS command (`init`, `log`, `show`, `diff`, `rerun`, `watch`, `data`, `idea`, `seal`, `report`, `remote`, `push`, `pull`, `plugin`, `shell`) validates a firm `ae_live_` key upfront via `alphaengine.auth.require_firm_key()`. The library, `alphaengine demo`, `alphaengine process`, and `ae verify` remain free and keyless.
264
+ - **OS keychain storage & secret redaction:** `auth.py` stores credentials in the OS keychain (`keyring`) when available with `~/.config/alphaengine/credentials.json` (`0600`) as fallback, and automatically redacts `ae_live_*` tokens from CLI output, errors, tracebacks, and `--json` envelopes.
265
+ - **Retail CLI path removed:** Removed `--retail` and `/api/retail/ledger/import` from the CLI; retail ledger import happens via web upload in the quantOS portal.
266
+ - **Unified canonical SHA-256 contract & golden fixture:** `canonical_sha256()` (Schema 1.2 sorted-key JSON with fixed `.8f` float formatting and full 64-hex SHA-256) powers `content_sha256`, study seals, CAS objects, dataset hashes, and parameter hashes. Legacy seals still verify and are labeled `"legacy"`. Shipped `alphaengine/contracts/hash_golden.json` in the wheel for cross-repo verification.
267
+ - **Project-wide cumulative trials & DSR everywhere (with re-run deduplication):** The project-wide cumulative trial ledger feeds every DSR — run output, headline `deflated_sharpe` and `n_trials`, push payloads, and sealed studies — with no per-run or per-family reset. Re-running an identical configuration (`command` + `params_hash` + `dataset_hashes`) records `duplicate_of` and adds `0` new trials (`9 -> 9`), while distinct configurations increment cumulative `N` (`9 -> 18`).
268
+ - **Metadata versioning & socket-blocked offline test:** `__version__` is read from `importlib.metadata.version("alphaengine")` with no hardcoded version strings, `combinatorial_oos_stability` and `PBO via CSCV` naming is standardized throughout, and `tests/test_smoke.py` enforces zero socket calls across DSR, PBO via CSCV, `combinatorial_oos_stability`, metrics, `sweep`, and loaders.
269
+ - **Free vs. Firm guide:** Added [`docs/free-vs-firm.md`](docs/free-vs-firm.md).
270
+
271
+ ---
272
+
238
273
  ## What's new in 1.1.0
239
274
 
240
275
  - **Local-first project workspace (`ae init`):** Creates `.alphaengine/` (`project.toml`, `ledger/`, `data/`, `studies/`, `ideas/`, `universes/`, and SHA-256 content-addressed `objects/`). Commands walk up from the current directory to find the project; outside a project, every command behaves as a standalone call.
@@ -248,7 +283,7 @@ ae report memo HEAD --format md
248
283
  ## What's new in 1.0.3
249
284
 
250
285
  - **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`.
251
- - **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.
286
+ - **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>` to ingest into quantOS.
252
287
  - **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$.
253
288
  - **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"`).
254
289
 
@@ -269,7 +304,7 @@ A portal that rejects the trace does not fail the run.
269
304
  ## What's new in 0.8.0
270
305
 
271
306
  Named processes, Grinold, and a frozen chart contract. Existing goldens
272
- (deflated Sharpe, PBO, performance, screen) are byte-identical. New figures
307
+ (deflated Sharpe, PBO via CSCV, performance, screen) are byte-identical. New figures
273
308
  are a public contract from this release. The study schema is **1.1**: optional
274
309
  `process` and `charts` fields; 1.0 studies still load.
275
310
 
@@ -298,7 +333,7 @@ a = grinold_alpha(panel, ic=0.05, vols=vols) # alpha stays here; missing vols a
298
333
  ## What's new in 0.7.0
299
334
 
300
335
  The daily modelling morning, and the overnight book, without a third
301
- dependency. Existing goldens (deflated Sharpe, PBO, performance, screen) are
336
+ dependency. Existing goldens (deflated Sharpe, PBO via CSCV, performance, screen) are
302
337
  byte-identical. New figures are a public contract from this release.
303
338
 
304
339
  | You have | You get | Module |
@@ -369,7 +404,7 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
369
404
  | `watch <pipeline.yaml> [--interval SECONDS]` | re-run a declarative pipeline whenever a registered dataset file changes | shell + session |
370
405
  | `seal <study.json \| run \| idea>` | write canonical content_sha256 into a study (or seal a project run/idea) | shell + session |
371
406
  | `verify <study.json> [--data FILE]` | verify a study.json content_sha256 seal and optionally re-check figures | shell + session |
372
- | `report <study.json> [--retail] \| memo <idea \| run> [--format md \| pdf]` | send a study to quantOS, or export a cited committee memo (md/pdf) | shell + session |
407
+ | `report <study.json> \| memo <idea \| run> [--format md \| pdf]` | send a study to quantOS (/api/me/runs/ingest), or export a cited committee memo | shell + session |
373
408
  | `<anything else>` | ask in plain English; your model picks a workflow and drives it | session |
374
409
  | `status` | the current run, and what is loaded | session |
375
410
 
@@ -430,7 +465,6 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
430
465
  | `--limit N` | how many rows to show (default 25) |
431
466
  | `--budget N` | how many runs a night is worth (default 3) |
432
467
  | `--purge` | remove ~/.config/alphaengine/credentials.json and clean up its config directory |
433
- | `--retail` | send the study to the retail experiment ledger (/api/retail/ledger/import) |
434
468
  | `--name NAME` | name for the initialized project or registered dataset |
435
469
  | `--idea ID` | attach the run or filter the log to a local hypothesis id |
436
470
  | `--family FAMILY` | strategy or experiment family for cumulative trial counting |
@@ -485,7 +519,6 @@ alphaengine seal HEAD
485
519
  alphaengine verify study.json
486
520
  alphaengine verify study.json --data prices.csv
487
521
  alphaengine report study.json
488
- alphaengine report study.json --retail
489
522
  alphaengine report memo HEAD --format md
490
523
  alphaengine models
491
524
  alphaengine trace
@@ -540,7 +573,7 @@ above exists to copy:
540
573
  per-period returns themselves. `sweep` does `np.asarray(list(raw))`, so a dict
541
574
  of results iterates its *keys* and fails on the first string.
542
575
 
543
- **Return the same length for every combination.** PBO splits the trial matrix
576
+ **Return the same length for every combination.** PBO via CSCV splits the trial matrix
544
577
  into time blocks and compares configurations within each block, which only means
545
578
  anything if they line up in time. Ragged output is refused rather than truncated,
546
579
  because silently trimming produces a confident number over series that do not
@@ -592,7 +625,7 @@ diffable, and versioned so it still parses in two years.
592
625
 
593
626
  | Module | Contents |
594
627
  |---|---|
595
- | `alphaengine.core` | deflated Sharpe, PSR, PBO via CSCV, CPCV, minimum track record length, performance and risk |
628
+ | `alphaengine.core` | deflated Sharpe, PSR, PBO via CSCV, `combinatorial_oos_stability`, minimum track record length, performance and risk |
596
629
  | `alphaengine.core.panel` | cross-sectional rank, z-score, winsorize, neutralize |
597
630
  | `alphaengine.core.signals` | IC, ICIR, quantile returns, decay |
598
631
  | `alphaengine.core.cross_section` | Fama-MacBeth, quantile book with turnover |
@@ -631,14 +664,15 @@ Study.from_sweep(r, label="momentum, 9 configs").report()
631
664
  What crosses is an explicit allowlist: the trial count and how it was obtained,
632
665
  a content hash of the data, the verdict, the shape of the neighbourhood, the
633
666
  performance figures. Computation runs locally; when you are signed in to
634
- quantOS, summary figures are reported to your account, and full series are
635
- shared only if you opt in (`--share-series` or `ALPHAENGINE_SHARE_SERIES=1`).
636
- Without opt-in, a guard keyed on length (at most 512 elements) refuses to send
667
+ quantOS, summary figures are reported to your account, and full return series
668
+ upload only with `--share-series` (or `ALPHAENGINE_SHARE_SERIES=1`). Without
669
+ `--share-series`, a guard keyed on length (at most 512 elements) refuses to send
637
670
  anything series-shaped whatever it is called.
638
671
 
639
- Reporting is the only part of this package that touches a network, so it is the
640
- only part that is not imported until you call it. `import alphaengine` still
641
- makes no network call.
672
+ ### What uses the network
673
+
674
+ - **Zero network calls (completely offline):** `import alphaengine`, `sweep()`, `Study.save()`, `Study.load()`, every math primitive in `alphaengine.core` (`deflated_sharpe`, `pbo_cscv`, `combinatorial_oos_stability`, performance, risk, covariance, allocation, process calibration/stress, Grinold), local loaders (`load_csv`, `from_quantconnect`, `from_vectorbt`), `alphaengine demo`, `alphaengine process`, and `ae verify`.
675
+ - **Uses the network (requires a firm `ae_live_` key from Connect):** `login` / `whoami`, CLI workflows (`validate`, `screen`, `diagnose`, `decompose`, `pairs`, `walkforward`, `compare`, `size`, `run`, `workflows`, `runs`, `gaps`, `tonight`), `Study.report()` / `ae report`, and workspace sync (`ae seal` key check, `ae push`, `ae pull`). Full return series upload **only** when `--share-series` is passed.
642
676
 
643
677
  ## Where this sits in QuantOS
644
678
 
@@ -2,9 +2,8 @@
2
2
 
3
3
  ## Reporting a vulnerability
4
4
 
5
- Report privately through [GitHub Private Vulnerability Reporting / Security Advisories](https://github.com/quantOSC/alphaengine/security/advisories/new),
6
- or email **wfe818@gmail.com**. Please don't open a public issue for a
7
- suspected vulnerability.
5
+ Report privately through [GitHub Private Vulnerability Reporting / Security Advisories](https://github.com/quantOSC/alphaengine/security/advisories/new).
6
+ Please don't open a public issue for a suspected vulnerability.
8
7
 
9
8
  We'll acknowledge within **3 working days** and give an assessment within **10**.
10
9
  If we accept the report we'll agree a disclosure timeline with you and credit
@@ -13,8 +12,8 @@ you in the advisory unless you'd rather we didn't.
13
12
  ## What's in scope
14
13
 
15
14
  This repository: the library, its build, and its release pipeline. The hosted
16
- platform is a separate system, report issues there to the same address and say
17
- so.
15
+ platform is a separate system; report issues there through the same private
16
+ vulnerability reporting channel and say so.
18
17
 
19
18
  ## How this package is built and shipped
20
19
 
@@ -0,0 +1,88 @@
1
+ # Free Open-Source Library vs. Firm CLI Workflows (`Connect`)
2
+
3
+ AlphaEngine has a strict, unambiguous boundary between what is **free and offline forever** for every researcher, and what requires a **firm `ae_live_` key** from **Connect** in the [quantOS portal](https://qosai.app).
4
+
5
+ There is **no retail CLI key** and **no retail CLI path**. Retail ledger import happens by web upload in the quantOS portal, not through the CLI.
6
+
7
+ ---
8
+
9
+ ## 1. Free, Keyless, and 100% Offline
10
+
11
+ Installing `pip install alphaengine` gives you the complete validation and research library with **no account, no API key, and zero network calls**:
12
+
13
+ | Surface | Commands / Entry Points | Network Calls | Key Required |
14
+ |---|---|---|---|
15
+ | **Python Library** | `import alphaengine`, `sweep()`, `Study`, `save()`, `load()` | **None** | **No** |
16
+ | **Validation Math** | `deflated_sharpe` (DSR), `probabilistic_sharpe_ratio` (PSR), `min_track_record_length`, `pbo_cscv` (PBO via CSCV), `combinatorial_oos_stability` | **None** | **No** |
17
+ | **Portfolio & Process Math** | `performance_report`, `compute_var_cvar`, `alphaengine.core.panel`, `signals`, `cross_section`, `covariance`, `allocate`, `process`, `active` | **None** | **No** |
18
+ | **Loaders** | `load_csv`, `load_delimited`, `from_quantconnect`, `from_vectorbt` | **None** | **No** |
19
+ | **Offline CLI Demos** | `alphaengine demo`, `alphaengine process` | **None** | **No** |
20
+ | **Study Verification** | `ae verify <study.json> [--data <file>]` | **None** | **No** |
21
+
22
+ Anyone receiving a sealed `study.json` (whether an academic referee, a peer, or an allocator) can run `ae verify study.json --data prices.csv` offline without an account to verify both the canonical `content_sha256` seal (including legacy seals) and the 64-hex `data_hash`.
23
+
24
+ ### Quickstart: Offline First (No Account Needed)
25
+
26
+ ```bash
27
+ pip install alphaengine
28
+
29
+ # 1. Run the built-in 9-trial parameter sweep and inspect the surface & DSR verdict
30
+ alphaengine demo
31
+
32
+ # 2. Fit OU, GBM, jump-diffusion, and GARCH(1,1) processes and run DGP stress offline
33
+ alphaengine process
34
+
35
+ # 3. Verify any sealed Study artifact (Schema 1.2 or legacy)
36
+ ae verify study.json
37
+ ```
38
+
39
+ ```python
40
+ from alphaengine import (
41
+ combinatorial_oos_stability,
42
+ compute_var_cvar,
43
+ deflated_sharpe,
44
+ pbo_cscv,
45
+ performance_report,
46
+ sweep,
47
+ )
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 2. Connect for Firms (`ae_live_` Key Required)
53
+
54
+ CLI workflows and the local-first Research OS (`ae init`, project ledgers, dataset registry, hypothesis tracking, sealing, memos, and workspace sync) are built for quantitative teams and require an `ae_live_` key issued to your firm through **Connect** in the quantOS portal:
55
+
56
+ - **Core workflows:** `diagnose`, `screen`, `signal`, `validate`, `stress`, `overlap`, `size`, `monitor`, `run`, `workflows`, `runs`, `gaps`, `tonight`
57
+ - **Research OS (`ae`):** `init`, `log`, `show`, `diff`, `rerun`, `watch`, `data`, `idea`, `seal`, `report`, `remote`, `push`, `pull`, `plugin`, `shell`
58
+
59
+ Every gated command checks `alphaengine.auth.require_firm_key()` before executing. The server is the authority: the key's firm tier is verified server-side on every request, and a key stops working if its firm leaves an active firm tier.
60
+
61
+ ### Quickstart: Connect for Firms
62
+
63
+ 1. Open your firm's [quantOS portal](https://qosai.app) → **Connect** and issue an `ae_live_...` key.
64
+ 2. Sign in once from your terminal (stored in your OS keychain via `keyring` when available, or `~/.config/alphaengine/credentials.json` with `0600` permissions as fallback):
65
+
66
+ ```bash
67
+ ae login
68
+ # or set: export QUANTOS_API_KEY="ae_live_..."
69
+ ```
70
+
71
+ 3. Initialize your firm project and run workflows with cumulative project-wide trial accounting:
72
+
73
+ ```bash
74
+ ae init --name equity-momentum
75
+ ae data add prices.csv --name equities --asof-col date
76
+ ae idea new "Cross-sectional 20d momentum holds net of costs" --kill-if "DSR < 0.9"
77
+ ae validate --project research.momentum --idea idea_1
78
+ ae seal HEAD
79
+ ae push
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 3. What Uses the Network (and What Never Leaves Your Machine)
85
+
86
+ - **Makes zero network calls:** The Python library (`import alphaengine`), all math functions, `sweep()`, `Study.save()` / `load()`, `alphaengine demo`, `alphaengine process`, and `ae verify`. Enforced in CI by a socket-blocked test (`tests/test_smoke.py`).
87
+ - **Uses the network:** `ae login` / `whoami`, CLI workflows (`validate`, `screen`, `diagnose`, etc.), `Study.report()` / `ae report`, and workspace sync (`ae push`, `ae pull`).
88
+ - **Return series boundary:** By default, only summary figures and canonical SHA-256 hashes cross the wire; any list longer than 512 elements is refused by both client and server guards. Full return series upload **only** when you explicitly pass `--share-series` (or set `ALPHAENGINE_SHARE_SERIES=1`).
@@ -71,9 +71,11 @@ Each `.jsonl` file under `ledger/`, `data/`, and `ideas/` contains one UTF-8 JSO
71
71
 
72
72
  ---
73
73
 
74
- ## 4. Discovery Rule
74
+ ## 4. Discovery Rule & Network Boundary
75
75
 
76
76
  When any `ae` / `alphaengine` command runs:
77
77
  1. It checks the current working directory and each parent directory for `.alphaengine/project.toml`.
78
78
  2. If found, the command attaches to that `Project` instance.
79
- 3. If not found, commands that do not strictly require a project (`demo`, `process`, `seal <study.json>`, `report`, `version`, etc.) run in standalone mode without touching the filesystem.
79
+ 3. If not found, commands that do not strictly require a project (`demo`, `process`, `verify <study.json>`, `version`, etc.) run in standalone mode without touching the filesystem.
80
+
81
+ **Network boundary:** The Python library (`import alphaengine`, `sweep`, DSR, PBO via CSCV, `combinatorial_oos_stability`, metrics, loaders), `alphaengine demo`, `alphaengine process`, and `ae verify` make no network calls. Network calls occur only during `login`/`whoami`, firm CLI workflows, `report`, and `seal`/`push`/`pull` against the configured remote. Full return series upload only when `--share-series` is explicitly passed; otherwise the 512-element wire guard rejects any series-length list.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "alphaengine"
7
- dynamic = ["version"]
7
+ version = "1.1.2"
8
8
  description = "Validated research tooling for investment strategies: deflation, overfitting detection, and honest trial counts."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -112,9 +112,6 @@ Documentation = "https://github.com/quantOSC/alphaengine#readme"
112
112
  Source = "https://github.com/quantOSC/alphaengine"
113
113
  Issues = "https://github.com/quantOSC/alphaengine/issues"
114
114
 
115
- [tool.hatch.version]
116
- path = "src/alphaengine/_version.py"
117
-
118
115
  [tool.hatch.build.targets.wheel]
119
116
  packages = ["src/alphaengine"]
120
117