alphaengine 0.7.0__tar.gz → 0.8.0__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 (88) hide show
  1. {alphaengine-0.7.0 → alphaengine-0.8.0}/AGENTS.md +8 -5
  2. {alphaengine-0.7.0 → alphaengine-0.8.0}/PKG-INFO +56 -8
  3. {alphaengine-0.7.0 → alphaengine-0.8.0}/README.md +55 -7
  4. alphaengine-0.8.0/docs/assets/session.png +0 -0
  5. alphaengine-0.8.0/docs/figure_contract.md +46 -0
  6. alphaengine-0.8.0/docs/workflows/evaluate_process.json +18 -0
  7. alphaengine-0.8.0/docs/workflows/research_week.json +29 -0
  8. alphaengine-0.8.0/docs/workflows/stress_study.json +14 -0
  9. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/_version.py +7 -9
  10. alphaengine-0.8.0/src/alphaengine/charts.py +56 -0
  11. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/cli.py +162 -25
  12. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/client/executor.py +190 -0
  13. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/commands.py +22 -0
  14. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/__init__.py +20 -1
  15. alphaengine-0.8.0/src/alphaengine/core/active.py +95 -0
  16. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/allocate.py +30 -6
  17. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/covariance.py +14 -0
  18. alphaengine-0.8.0/src/alphaengine/core/process.py +450 -0
  19. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/repl.py +1 -0
  20. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/study/report.py +4 -0
  21. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/study/schema.py +7 -1
  22. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_cli.py +18 -0
  23. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_commands.py +2 -2
  24. alphaengine-0.8.0/tests/test_process.py +201 -0
  25. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_sweep.py +27 -0
  26. alphaengine-0.7.0/docs/assets/mark.png +0 -0
  27. alphaengine-0.7.0/docs/assets/session.png +0 -0
  28. {alphaengine-0.7.0 → alphaengine-0.8.0}/.github/workflows/ci.yml +0 -0
  29. {alphaengine-0.7.0 → alphaengine-0.8.0}/.github/workflows/publish.yml +0 -0
  30. {alphaengine-0.7.0 → alphaengine-0.8.0}/.gitignore +0 -0
  31. {alphaengine-0.7.0 → alphaengine-0.8.0}/LICENSE +0 -0
  32. {alphaengine-0.7.0 → alphaengine-0.8.0}/SECURITY.md +0 -0
  33. {alphaengine-0.7.0 → alphaengine-0.8.0}/docs/assets/banner.png +0 -0
  34. {alphaengine-0.7.0 → alphaengine-0.8.0}/docs/assets/data_boundary.png +0 -0
  35. {alphaengine-0.7.0 → alphaengine-0.8.0}/pyproject.toml +0 -0
  36. {alphaengine-0.7.0 → alphaengine-0.8.0}/scripts/gen_docs.py +0 -0
  37. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/__init__.py +0 -0
  38. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/__main__.py +0 -0
  39. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/agent/__init__.py +0 -0
  40. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/agent/answer.py +0 -0
  41. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/agent/driver.py +0 -0
  42. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/auth.py +0 -0
  43. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/book.py +0 -0
  44. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/client/__init__.py +0 -0
  45. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/client/agent.py +0 -0
  46. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/client/session.py +0 -0
  47. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/complete.py +0 -0
  48. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/connectors/__init__.py +0 -0
  49. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/backtest.py +0 -0
  50. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/cross_section.py +0 -0
  51. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/factors.py +0 -0
  52. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/pairs.py +0 -0
  53. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/panel.py +0 -0
  54. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/performance.py +0 -0
  55. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/profile.py +0 -0
  56. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/risk.py +0 -0
  57. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/screen.py +0 -0
  58. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/series_shapes.py +0 -0
  59. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/signals.py +0 -0
  60. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/stress.py +0 -0
  61. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/technical.py +0 -0
  62. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/validation.py +0 -0
  63. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/core/walkforward.py +0 -0
  64. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/demo.py +0 -0
  65. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/demo_book.py +0 -0
  66. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/demo_returns.py +0 -0
  67. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/demo_signal.py +0 -0
  68. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/demo_universe.py +0 -0
  69. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/events.py +0 -0
  70. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/loaders.py +0 -0
  71. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/model.py +0 -0
  72. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/py.typed +0 -0
  73. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/study/__init__.py +0 -0
  74. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/sweep/__init__.py +0 -0
  75. {alphaengine-0.7.0 → alphaengine-0.8.0}/src/alphaengine/sweep/runner.py +0 -0
  76. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_agent.py +0 -0
  77. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_answer.py +0 -0
  78. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_book.py +0 -0
  79. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_client.py +0 -0
  80. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_events.py +0 -0
  81. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_executor_ops.py +0 -0
  82. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_goldens.py +0 -0
  83. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_loaders.py +0 -0
  84. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_model.py +0 -0
  85. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_quant_maths.py +0 -0
  86. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_screen.py +0 -0
  87. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_signals.py +0 -0
  88. {alphaengine-0.7.0 → alphaengine-0.8.0}/tests/test_smoke.py +0 -0
@@ -56,13 +56,16 @@ animated parameter-surface canvas at boot and while Working) plus
56
56
  that is a version bump (`_version.py`).
57
57
  - **Compute ops on the executor** include the 0.6 workflow set (`backtest`,
58
58
  `score_backtest`, `cpcv`, `factors`, `pairs`, `cointegrated_pairs`,
59
- `walk_forward`, `book_overlap`) and the 0.7 CS / book set
59
+ `walk_forward`, `book_overlap`), the 0.7 CS / book set
60
60
  (`panel_transform`, `signal_icir`, `fama_macbeth`, `quantile_book`,
61
- `ewma_cov`, `denoise_cov`, `hrp`, `risk_parity`, `vol_target`). Panels and
62
- covariance matrices stay in the workspace; figures go over the wire. The
61
+ `ewma_cov`, `denoise_cov`, `hrp`, `risk_parity`, `vol_target`), and the
62
+ 0.8 process / Grinold set (`ou_calibrate`, `ou_simulate`, `gbm_calibrate`,
63
+ `jump_calibrate`, `garch`, `dgp_stress`, `grinold_alpha`, `breadth_ir`,
64
+ `detone_cov`). Panels, covariance matrices, GARCH variance paths, OU paths
65
+ and alpha vectors stay in the workspace; figures go over the wire. The
63
66
  portal must offer an op in a workflow graph or it sits unused; the CLI
64
- remains useful without them. Short names in a panel are skipped and counted,
65
- they do not shrink the rest of the book.
67
+ `process` command runs the DGP stress offline without a portal. Short names
68
+ in a panel are skipped and counted, they do not shrink the rest of the book.
66
69
  - `sweep(..., jobs=N)` defaults to 1. Greater than 1 is opt-in and must keep
67
70
  trial index identity, including failures.
68
71
  - The `connectors` extra is lazy: parquet via `pyarrow`, HTTP via `httpx` to a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: alphaengine
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: Validated research tooling for investment strategies: deflation, overfitting detection, and honest trial counts.
5
5
  Project-URL: Homepage, https://github.com/quantOSC/alphaengine
6
6
  Project-URL: Documentation, https://github.com/quantOSC/alphaengine#readme
@@ -60,17 +60,14 @@ Description-Content-Type: text/markdown
60
60
  <img src="https://img.shields.io/badge/deps-numpy%20%2B%20scipy-C4893A?style=flat-square" alt="Two dependencies: numpy and scipy">
61
61
  </p>
62
62
 
63
- <p align="center">
64
- <img src="docs/assets/mark.png" alt="AlphaEngine mark" width="96">
65
- </p>
66
-
67
63
  ```bash
68
64
  pip install alphaengine
69
65
  alphaengine demo # the whole offline half, no account, no data of your own
66
+ alphaengine process # fit a DGP to the demo walk and stress it
70
67
  ```
71
68
 
72
69
  <p align="center">
73
- <img src="docs/assets/session.png" alt="The session canvas: a living parameter surface, then demo, login, load" width="92%">
70
+ <img src="docs/assets/session.png" alt="The session canvas: tracked ALPHAENGINE over a teal plateau and amber ridge, then your data never leaves." width="92%">
74
71
  </p>
75
72
 
76
73
  The session is the product. Sign in, load something, then ask:
@@ -196,6 +193,35 @@ distinction is the whole of the data boundary below.
196
193
 
197
194
  ---
198
195
 
196
+ ## What's new in 0.8.0
197
+
198
+ Named processes, Grinold, and a frozen chart contract. Existing goldens
199
+ (deflated Sharpe, PBO, performance, screen) are byte-identical. New figures
200
+ are a public contract from this release. The study schema is **1.1**: optional
201
+ `process` and `charts` fields; 1.0 studies still load.
202
+
203
+ | You have | You get | Module |
204
+ |---|---|---|
205
+ | A spread or a close | OU / GBM / jump / GARCH(1,1) calibration, then Monte Carlo stress under that law | `core.process` |
206
+ | An IC and a cross-section | Grinold alpha, breadth, transfer coefficient | `core.active` |
207
+ | A return panel | Detoned covariance and a PCA variance-explained table | `core.covariance` |
208
+ | Figures that already travel | `{kind, key, title}` hints a portal maps to a chart | `charts`, [figure contract](docs/figure_contract.md) |
209
+
210
+ The terminal boot is the README banner: tracked `ALPHAENGINE`, the subtitle,
211
+ a teal-left / amber-right mesh (plateau, valley, knife-edge ridge), and
212
+ `your data never leaves.` `alphaengine process` fits a DGP offline, no account.
213
+
214
+ ```python
215
+ from alphaengine.core import ou_calibrate, garch_calibrate, dgp_stress, grinold_alpha
216
+
217
+ ou = ou_calibrate(spread) # kappa, theta, half_life; or not mean-reverting
218
+ g = garch_calibrate(returns) # omega, alpha, beta, persistence in (0, 1)
219
+ s = dgp_stress(close, dgp="ou") # n_trials = n_paths, source monte_carlo
220
+ a = grinold_alpha(panel, ic=0.05) # alpha vector stays here; scalars travel
221
+ ```
222
+
223
+ ---
224
+
199
225
  ## What's new in 0.7.0
200
226
 
201
227
  The daily modelling morning, and the overnight book, without a third
@@ -237,6 +263,7 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
237
263
  | Command | Does | Where |
238
264
  |---|---|---|
239
265
  | `demo` | run the built-in example offline, with no account and no data | shell + session |
266
+ | `process [ou \| gbm \| jump \| garch]` | fit a named process to a series and stress it, offline | shell + session |
240
267
  | `runs [--limit N]` | your own week: what ran, what it decided, what it filed | shell + session |
241
268
  | `gaps` | what your record says is UNANSWERED, and what closes each one | shell + session |
242
269
  | `tonight [--budget N]` | what would run unattended tonight, without running any of it | shell + session |
@@ -320,6 +347,8 @@ alphaengine overlap
320
347
  alphaengine size
321
348
  alphaengine monitor
322
349
  alphaengine demo
350
+ alphaengine process
351
+ alphaengine process ou
323
352
  alphaengine runs
324
353
  alphaengine runs --limit 50
325
354
  alphaengine gaps
@@ -423,8 +452,11 @@ diffable, and versioned so it still parses in two years.
423
452
  | `alphaengine.core.panel` | cross-sectional rank, z-score, winsorize, neutralize |
424
453
  | `alphaengine.core.signals` | IC, ICIR, quantile returns, decay |
425
454
  | `alphaengine.core.cross_section` | Fama-MacBeth, quantile book with turnover |
426
- | `alphaengine.core.covariance` | EWMA, Ledoit-Wolf, Marchenko-Pastur denoise, detone |
427
- | `alphaengine.core.allocate` | HRP, risk parity, vol target |
455
+ | `alphaengine.core.covariance` | EWMA, Ledoit-Wolf, Marchenko-Pastur denoise, detone, variance explained |
456
+ | `alphaengine.core.allocate` | HRP, risk parity, vol target (EWMA or GARCH) |
457
+ | `alphaengine.core.process` | OU, GBM, jumps, GARCH(1,1), DGP stress |
458
+ | `alphaengine.core.active` | Grinold alpha, breadth, transfer coefficient |
459
+ | `alphaengine.charts` | frozen `{kind, key, title}` hints; see [figure contract](docs/figure_contract.md) |
428
460
  | `alphaengine.sweep` | the grid runner and the sensitivity surface |
429
461
  | `alphaengine.study` | the study artifact and its schema |
430
462
  | `alphaengine.client` | the workflow client and the step executor |
@@ -518,6 +550,22 @@ Management* 42(1), 13 to 28.
518
550
  Fama, E. F., and MacBeth, J. D. (1973). "Risk, Return, and Equilibrium:
519
551
  Empirical Tests." *Journal of Political Economy* 81(3), 607 to 636.
520
552
 
553
+ **Ornstein-Uhlenbeck / Vasicek**
554
+ Vasicek, O. (1977). "An Equilibrium Characterization of the Term Structure."
555
+ *Journal of Financial Economics* 5(2), 177 to 188.
556
+
557
+ **GARCH(1,1)**
558
+ Bollerslev, T. (1986). "Generalized Autoregressive Conditional
559
+ Heteroskedasticity." *Journal of Econometrics* 31(3), 307 to 327.
560
+
561
+ **Jump-diffusion**
562
+ Merton, R. C. (1976). "Option Pricing When Underlying Stock Returns Are
563
+ Discontinuous." *Journal of Financial Economics* 3(1-2), 125 to 144.
564
+
565
+ **The fundamental law of active management**
566
+ Grinold, R. C., and Kahn, R. N. (2000). *Active Portfolio Management.* 2nd ed.
567
+ McGraw-Hill.
568
+
521
569
  **Downside deviation**
522
570
  Sortino, F. A., and Price, L. N. (1994). "Performance Measurement in a Downside
523
571
  Risk Framework." *Journal of Investing* 3(3), 59 to 64.
@@ -18,17 +18,14 @@
18
18
  <img src="https://img.shields.io/badge/deps-numpy%20%2B%20scipy-C4893A?style=flat-square" alt="Two dependencies: numpy and scipy">
19
19
  </p>
20
20
 
21
- <p align="center">
22
- <img src="docs/assets/mark.png" alt="AlphaEngine mark" width="96">
23
- </p>
24
-
25
21
  ```bash
26
22
  pip install alphaengine
27
23
  alphaengine demo # the whole offline half, no account, no data of your own
24
+ alphaengine process # fit a DGP to the demo walk and stress it
28
25
  ```
29
26
 
30
27
  <p align="center">
31
- <img src="docs/assets/session.png" alt="The session canvas: a living parameter surface, then demo, login, load" width="92%">
28
+ <img src="docs/assets/session.png" alt="The session canvas: tracked ALPHAENGINE over a teal plateau and amber ridge, then your data never leaves." width="92%">
32
29
  </p>
33
30
 
34
31
  The session is the product. Sign in, load something, then ask:
@@ -154,6 +151,35 @@ distinction is the whole of the data boundary below.
154
151
 
155
152
  ---
156
153
 
154
+ ## What's new in 0.8.0
155
+
156
+ Named processes, Grinold, and a frozen chart contract. Existing goldens
157
+ (deflated Sharpe, PBO, performance, screen) are byte-identical. New figures
158
+ are a public contract from this release. The study schema is **1.1**: optional
159
+ `process` and `charts` fields; 1.0 studies still load.
160
+
161
+ | You have | You get | Module |
162
+ |---|---|---|
163
+ | A spread or a close | OU / GBM / jump / GARCH(1,1) calibration, then Monte Carlo stress under that law | `core.process` |
164
+ | An IC and a cross-section | Grinold alpha, breadth, transfer coefficient | `core.active` |
165
+ | A return panel | Detoned covariance and a PCA variance-explained table | `core.covariance` |
166
+ | Figures that already travel | `{kind, key, title}` hints a portal maps to a chart | `charts`, [figure contract](docs/figure_contract.md) |
167
+
168
+ The terminal boot is the README banner: tracked `ALPHAENGINE`, the subtitle,
169
+ a teal-left / amber-right mesh (plateau, valley, knife-edge ridge), and
170
+ `your data never leaves.` `alphaengine process` fits a DGP offline, no account.
171
+
172
+ ```python
173
+ from alphaengine.core import ou_calibrate, garch_calibrate, dgp_stress, grinold_alpha
174
+
175
+ ou = ou_calibrate(spread) # kappa, theta, half_life; or not mean-reverting
176
+ g = garch_calibrate(returns) # omega, alpha, beta, persistence in (0, 1)
177
+ s = dgp_stress(close, dgp="ou") # n_trials = n_paths, source monte_carlo
178
+ a = grinold_alpha(panel, ic=0.05) # alpha vector stays here; scalars travel
179
+ ```
180
+
181
+ ---
182
+
157
183
  ## What's new in 0.7.0
158
184
 
159
185
  The daily modelling morning, and the overnight book, without a third
@@ -195,6 +221,7 @@ w = hrp_weights(cov, names=names) # no matrix inverse; weights sum to one
195
221
  | Command | Does | Where |
196
222
  |---|---|---|
197
223
  | `demo` | run the built-in example offline, with no account and no data | shell + session |
224
+ | `process [ou \| gbm \| jump \| garch]` | fit a named process to a series and stress it, offline | shell + session |
198
225
  | `runs [--limit N]` | your own week: what ran, what it decided, what it filed | shell + session |
199
226
  | `gaps` | what your record says is UNANSWERED, and what closes each one | shell + session |
200
227
  | `tonight [--budget N]` | what would run unattended tonight, without running any of it | shell + session |
@@ -278,6 +305,8 @@ alphaengine overlap
278
305
  alphaengine size
279
306
  alphaengine monitor
280
307
  alphaengine demo
308
+ alphaengine process
309
+ alphaengine process ou
281
310
  alphaengine runs
282
311
  alphaengine runs --limit 50
283
312
  alphaengine gaps
@@ -381,8 +410,11 @@ diffable, and versioned so it still parses in two years.
381
410
  | `alphaengine.core.panel` | cross-sectional rank, z-score, winsorize, neutralize |
382
411
  | `alphaengine.core.signals` | IC, ICIR, quantile returns, decay |
383
412
  | `alphaengine.core.cross_section` | Fama-MacBeth, quantile book with turnover |
384
- | `alphaengine.core.covariance` | EWMA, Ledoit-Wolf, Marchenko-Pastur denoise, detone |
385
- | `alphaengine.core.allocate` | HRP, risk parity, vol target |
413
+ | `alphaengine.core.covariance` | EWMA, Ledoit-Wolf, Marchenko-Pastur denoise, detone, variance explained |
414
+ | `alphaengine.core.allocate` | HRP, risk parity, vol target (EWMA or GARCH) |
415
+ | `alphaengine.core.process` | OU, GBM, jumps, GARCH(1,1), DGP stress |
416
+ | `alphaengine.core.active` | Grinold alpha, breadth, transfer coefficient |
417
+ | `alphaengine.charts` | frozen `{kind, key, title}` hints; see [figure contract](docs/figure_contract.md) |
386
418
  | `alphaengine.sweep` | the grid runner and the sensitivity surface |
387
419
  | `alphaengine.study` | the study artifact and its schema |
388
420
  | `alphaengine.client` | the workflow client and the step executor |
@@ -476,6 +508,22 @@ Management* 42(1), 13 to 28.
476
508
  Fama, E. F., and MacBeth, J. D. (1973). "Risk, Return, and Equilibrium:
477
509
  Empirical Tests." *Journal of Political Economy* 81(3), 607 to 636.
478
510
 
511
+ **Ornstein-Uhlenbeck / Vasicek**
512
+ Vasicek, O. (1977). "An Equilibrium Characterization of the Term Structure."
513
+ *Journal of Financial Economics* 5(2), 177 to 188.
514
+
515
+ **GARCH(1,1)**
516
+ Bollerslev, T. (1986). "Generalized Autoregressive Conditional
517
+ Heteroskedasticity." *Journal of Econometrics* 31(3), 307 to 327.
518
+
519
+ **Jump-diffusion**
520
+ Merton, R. C. (1976). "Option Pricing When Underlying Stock Returns Are
521
+ Discontinuous." *Journal of Financial Economics* 3(1-2), 125 to 144.
522
+
523
+ **The fundamental law of active management**
524
+ Grinold, R. C., and Kahn, R. N. (2000). *Active Portfolio Management.* 2nd ed.
525
+ McGraw-Hill.
526
+
479
527
  **Downside deviation**
480
528
  Sortino, F. A., and Price, L. N. (1994). "Performance Measurement in a Downside
481
529
  Risk Framework." *Journal of Investing* 3(3), 59 to 64.
@@ -0,0 +1,46 @@
1
+ # Figure contract
2
+
3
+ A figure that travels is a **named kind**, not a series. The executor attaches
4
+ `charts: [{kind, key, title}]` hints. The portal maps `kind` + `key` to a
5
+ component. Old portals ignore `charts` and still have the raw keys
6
+ (`ic_by_period`, `best_curve`, …).
7
+
8
+ No hint carries a nested series. Titles are one line, no em dash.
9
+
10
+ Study schema **1.1** may also store `charts` and `process` on the artifact.
11
+ Both are optional. A 1.0 study still loads.
12
+
13
+ ## Kinds
14
+
15
+ | Kind | Payload | Draws as |
16
+ |---|---|---|
17
+ | `curve` | `{i, v}` | line |
18
+ | `period` | `{p, ic}` or `{p, lambda}` | CS time series |
19
+ | `band` | `{i, lo, mid, hi}` | fan / Monte Carlo quantile |
20
+ | `hist` | `{edges, counts}` | histogram |
21
+ | `cost` | `{bps, sharpe}` | cost ladder |
22
+ | `scatter` | `{x, y}` | overlap |
23
+ | `triangle` | `{a, b, v}` | covariance heatmap |
24
+ | `rows` | bounded dict rows | table |
25
+ | `quantiles` | `[float]` | bar |
26
+
27
+ `i` is a position index into the original series, never a timestamp. Timestamps
28
+ stay on the machine that holds the data.
29
+
30
+ Lists longer than 512 are a series, not a figure, and the wire guard refuses
31
+ them. Sketches (`path_sketch`, `vol_path`, `mean_path`, `band`) are bucketed
32
+ below that cap before they travel.
33
+
34
+ ## Who emits what
35
+
36
+ | Op | Typical hints |
37
+ |---|---|
38
+ | `compute.ou_calibrate` | `curve` / `path_sketch` |
39
+ | `compute.ou_simulate` | `curve` / `mean_path`, `band` / `band` |
40
+ | `compute.garch` | `curve` / `vol_path` |
41
+ | `compute.dgp_stress` | `hist` / `sharpe_hist` |
42
+ | `compute.denoise_cov` | `triangle` / `triangle`, `rows` / `variance_explained` |
43
+ | `compute.detone_cov` | same |
44
+ | `compute.grinold_alpha` | scalars only; the alpha vector stays in the workspace |
45
+
46
+ The Python helper is `alphaengine.charts.chart(kind, key, title)`.
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "evaluate_process",
3
+ "schema": "alphaengine.workflow/1",
4
+ "title": "what law describes this path?",
5
+ "reproducible": true,
6
+ "needs": ["data"],
7
+ "selection": "all",
8
+ "note": "Portal harness copies this. Agents still only index permitted; these ops sit unused until registered.",
9
+ "stages": [
10
+ {"op": "data.resolve"},
11
+ {"op": "compute.gbm_calibrate"},
12
+ {"op": "compute.ou_calibrate"},
13
+ {"op": "compute.jump_calibrate"},
14
+ {"op": "compute.garch"},
15
+ {"op": "compute.dgp_stress", "params": {"dgp": "gbm"}},
16
+ {"op": "emit.process_report"}
17
+ ]
18
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "research_week",
3
+ "schema": "alphaengine.workflow/1",
4
+ "title": "gaps the modeling suite closes once the portal registers the ops",
5
+ "gaps": [
6
+ {
7
+ "question": "what law describes this path?",
8
+ "workflow": "evaluate_process",
9
+ "op": "compute.dgp_stress",
10
+ "cli": "alphaengine process"
11
+ },
12
+ {
13
+ "question": "where does it break under a named DGP?",
14
+ "workflow": "stress_study",
15
+ "stage": "dgp_stress",
16
+ "cli": "alphaengine process ou"
17
+ },
18
+ {
19
+ "question": "how much alpha does the IC imply?",
20
+ "workflow": "size_position",
21
+ "op": "compute.grinold_alpha"
22
+ },
23
+ {
24
+ "question": "is the book the market in disguise?",
25
+ "workflow": "size_position",
26
+ "op": "compute.detone_cov"
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "stress_study",
3
+ "schema": "alphaengine.workflow/1",
4
+ "title": "where does it break?",
5
+ "note": "Additive DGP stage for the existing stress_study graph. Copy into the portal harness. The CLI spelling that does not wait on the portal is `alphaengine process` or `stress --input dgp=ou` once this stage is live.",
6
+ "add_stage": {
7
+ "id": "dgp_stress",
8
+ "op": "compute.dgp_stress",
9
+ "input": "dgp",
10
+ "default": "gbm",
11
+ "values": ["ou", "gbm", "jump", "garch"],
12
+ "after": "compute.cost_ladder"
13
+ }
14
+ }
@@ -63,12 +63,10 @@ is what a changed figure costs. The API may still move underneath it.
63
63
  # doors, same refusals. The published artefact is a new wheel because 0.6.0
64
64
  # already left the building.
65
65
  #
66
- # 0.7.0 IS NEW PUBLIC FIGURES, AND A SIMPLER OS AROUND THEM. Existing DSR /
67
- # PBO / performance / screen goldens stay byte-identical. The artefact grew
68
- # the daily cross-section (rank, z-score, winsorize, neutralize, ICIR,
69
- # Fama-MacBeth, quantile book with turnover) and the overnight book
70
- # (EWMA / Ledoit-Wolf / Marchenko-Pastur covariance, HRP, risk parity, vol
71
- # target), plus a living parameter-surface canvas and one-verb `login` /
72
- # `load`. New numbers are a public contract from this release; old numbers
73
- # did not move.
74
- __version__ = "0.7.0"
66
+ # 0.8.0 IS THE MODELING SUITE THE REST OF THE LIBRARY WAS MISSING. Existing
67
+ # DSR / PBO / performance / screen goldens stay byte-identical. The artefact
68
+ # grew named processes (OU, GBM, jumps, GARCH), DGP stress with an honest
69
+ # Monte Carlo trial count, Grinold alpha / breadth / TC, a frozen chart
70
+ # vocabulary, and a terminal boot that is the README banner in glyphs.
71
+ # New figures are a public contract from this release; old numbers did not move.
72
+ __version__ = "0.8.0"
@@ -0,0 +1,56 @@
1
+ """Frozen chart vocabulary the portal draws from figures.
2
+
3
+ A FIGURE IS A NAMED KIND, not a series. The executor attaches
4
+ `charts: [{kind, key, title}]` hints. The portal maps kind+key to a component.
5
+ Old portals ignore `charts` and still have the raw keys (`ic_by_period`,
6
+ `best_curve`, ...).
7
+
8
+ KINDS
9
+ curve {i, v} line
10
+ period {p, ic|lambda} CS time series
11
+ band {i, lo, mid, hi} fan / Monte Carlo quantile
12
+ hist {edges, counts} histogram
13
+ cost {bps, sharpe} cost ladder
14
+ scatter {x, y} overlap
15
+ triangle {a, b, v} covariance heatmap
16
+ rows bounded dict rows table
17
+ quantiles [float] bar
18
+
19
+ No hint may carry a nested series. Titles are one line, no em dash.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ __all__ = ["CHART_KINDS", "chart", "charts"]
25
+
26
+ CHART_KINDS = frozenset(
27
+ {
28
+ "curve",
29
+ "period",
30
+ "band",
31
+ "hist",
32
+ "cost",
33
+ "scatter",
34
+ "triangle",
35
+ "rows",
36
+ "quantiles",
37
+ }
38
+ )
39
+
40
+
41
+ def chart(kind: str, key: str, title: str) -> dict[str, str]:
42
+ """One portal hint. `kind` must be in CHART_KINDS."""
43
+ k = str(kind)
44
+ if k not in CHART_KINDS:
45
+ raise ValueError(f"unknown chart kind {k!r}; expected one of {sorted(CHART_KINDS)}")
46
+ return {"kind": k, "key": str(key), "title": str(title)}
47
+
48
+
49
+ def charts(*items: dict[str, str]) -> list[dict[str, str]]:
50
+ """Hint list. Empty is allowed: the portal then uses raw keys only."""
51
+ out: list[dict[str, str]] = []
52
+ for item in items:
53
+ if not item:
54
+ continue
55
+ out.append(chart(item["kind"], item["key"], item["title"]) if "kind" in item else dict(item))
56
+ return out