trade-study 0.1.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 (31) hide show
  1. trade_study-0.1.0/LICENSE +21 -0
  2. trade_study-0.1.0/PKG-INFO +279 -0
  3. trade_study-0.1.0/README.md +218 -0
  4. trade_study-0.1.0/pyproject.toml +132 -0
  5. trade_study-0.1.0/setup.cfg +4 -0
  6. trade_study-0.1.0/src/trade_study/__init__.py +54 -0
  7. trade_study-0.1.0/src/trade_study/_pareto.py +128 -0
  8. trade_study-0.1.0/src/trade_study/_scoring.py +213 -0
  9. trade_study-0.1.0/src/trade_study/_version.py +1 -0
  10. trade_study-0.1.0/src/trade_study/design.py +309 -0
  11. trade_study-0.1.0/src/trade_study/io.py +67 -0
  12. trade_study-0.1.0/src/trade_study/protocols.py +130 -0
  13. trade_study-0.1.0/src/trade_study/py.typed +0 -0
  14. trade_study-0.1.0/src/trade_study/runner.py +170 -0
  15. trade_study-0.1.0/src/trade_study/stacking.py +100 -0
  16. trade_study-0.1.0/src/trade_study/study.py +211 -0
  17. trade_study-0.1.0/src/trade_study.egg-info/PKG-INFO +279 -0
  18. trade_study-0.1.0/src/trade_study.egg-info/SOURCES.txt +29 -0
  19. trade_study-0.1.0/src/trade_study.egg-info/dependency_links.txt +1 -0
  20. trade_study-0.1.0/src/trade_study.egg-info/requires.txt +48 -0
  21. trade_study-0.1.0/src/trade_study.egg-info/top_level.txt +1 -0
  22. trade_study-0.1.0/tests/test_api.py +67 -0
  23. trade_study-0.1.0/tests/test_design.py +374 -0
  24. trade_study-0.1.0/tests/test_io.py +208 -0
  25. trade_study-0.1.0/tests/test_pareto.py +275 -0
  26. trade_study-0.1.0/tests/test_protocols.py +300 -0
  27. trade_study-0.1.0/tests/test_runner.py +322 -0
  28. trade_study-0.1.0/tests/test_scoring.py +170 -0
  29. trade_study-0.1.0/tests/test_smoke.py +8 -0
  30. trade_study-0.1.0/tests/test_stacking.py +179 -0
  31. trade_study-0.1.0/tests/test_study.py +528 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joshua C. Macdonald
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.4
2
+ Name: trade-study
3
+ Version: 0.1.0
4
+ Summary: Multi-objective trade-study orchestration: scoring, Pareto optimization, and Bayesian stacking
5
+ Author-email: "Joshua C. Macdonald" <jmacdo16@jh.edu>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jcm-sci/trade-study
8
+ Project-URL: Documentation, https://jcm-sci.github.io/trade-study/
9
+ Project-URL: Repository, https://github.com/jcm-sci/trade-study
10
+ Project-URL: Changelog, https://github.com/jcm-sci/trade-study/blob/main/CHANGELOG.md
11
+ Project-URL: Issues, https://github.com/jcm-sci/trade-study/issues
12
+ Keywords: trade-study,model-selection,scoring-rules,pareto,bayesian-stacking,design-of-experiments,sensitivity-analysis,multi-objective-optimization
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: numpy>=1.24
28
+ Provides-Extra: scoring
29
+ Requires-Dist: scoringrules>=0.8; extra == "scoring"
30
+ Provides-Extra: pareto
31
+ Requires-Dist: pymoo>=0.6; extra == "pareto"
32
+ Provides-Extra: stacking
33
+ Requires-Dist: arviz>=0.20; python_version < "3.12" and extra == "stacking"
34
+ Requires-Dist: arviz>=1.0; python_version >= "3.12" and extra == "stacking"
35
+ Requires-Dist: scipy>=1.10; extra == "stacking"
36
+ Provides-Extra: design
37
+ Requires-Dist: pyDOE3>=1.5; extra == "design"
38
+ Requires-Dist: SALib>=1.5; extra == "design"
39
+ Requires-Dist: scipy>=1.10; extra == "design"
40
+ Provides-Extra: adaptive
41
+ Requires-Dist: optuna>=4.0; extra == "adaptive"
42
+ Provides-Extra: parallel
43
+ Requires-Dist: joblib>=1.3; extra == "parallel"
44
+ Provides-Extra: all
45
+ Requires-Dist: trade-study[adaptive,design,parallel,pareto,scoring,stacking]; extra == "all"
46
+ Provides-Extra: examples
47
+ Requires-Dist: scikit-learn>=1.3; extra == "examples"
48
+ Requires-Dist: trade-study[all]; extra == "examples"
49
+ Provides-Extra: test
50
+ Requires-Dist: pytest>=8.1; extra == "test"
51
+ Requires-Dist: pytest-cov>=6.0; extra == "test"
52
+ Requires-Dist: trade-study[all]; extra == "test"
53
+ Provides-Extra: docs
54
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
55
+ Requires-Dist: mkdocstrings[python]>=0.27; extra == "docs"
56
+ Provides-Extra: dev
57
+ Requires-Dist: mypy>=1.18; extra == "dev"
58
+ Requires-Dist: ruff>=0.13; extra == "dev"
59
+ Requires-Dist: trade-study[docs,test]; extra == "dev"
60
+ Dynamic: license-file
61
+
62
+ # trade-study
63
+
64
+ [![CI](https://github.com/jcm-sci/trade-study/actions/workflows/ci.yml/badge.svg)](https://github.com/jcm-sci/trade-study/actions/workflows/ci.yml)
65
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
66
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
67
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
68
+ [![jcm-sci](https://img.shields.io/badge/jcm--sci-jcmacdonald.dev-blue)](https://jcmacdonald.dev/software/)
69
+
70
+ Multi-objective trade-study orchestration: define factors, build
71
+ parameter grids, run hierarchical study phases, and extract Pareto
72
+ fronts — for any domain where you compare alternatives against
73
+ competing objectives.
74
+
75
+ ## Statement of need
76
+
77
+ Comparing design alternatives against multiple objectives is a common
78
+ task across scientific simulation, engineering trade-offs, and ML
79
+ hyperparameter tuning. Researchers typically glue together separate
80
+ tools for grid construction, execution, scoring, and Pareto analysis,
81
+ writing ad-hoc scripts that are hard to reproduce or extend to
82
+ multi-phase studies (screening → refinement → benchmark).
83
+
84
+ `trade-study` provides a single orchestration layer that composes these
85
+ steps into a reproducible, protocol-driven workflow. Users supply a
86
+ `Simulator` (generates data) and a `Scorer` (evaluates it); the
87
+ framework handles grid construction, parallel execution, Pareto
88
+ extraction, and phase chaining. All components are modular and
89
+ optional — use only what you need.
90
+
91
+ This package targets researchers and practitioners who need:
92
+
93
+ - structured multi-phase experimental design (screen → refine → benchmark),
94
+ - multi-objective Pareto analysis across heterogeneous factors, and
95
+ - a reproducible Python API that separates domain logic from study orchestration.
96
+
97
+ ## Why trade-study?
98
+
99
+ | Need | Without trade-study | With trade-study |
100
+ |------|---------------------|------------------|
101
+ | Parameter grid | Manual `itertools.product` or one-off scripts | `build_grid(factors, method="sobol")` — full factorial, LHS, Sobol, Halton |
102
+ | Multi-objective ranking | Call pymoo directly, handle direction normalization | `extract_front(scores, directions)` — direction-aware |
103
+ | Phased studies | Custom loop with manual filtering between stages | `Study(phases=[Phase(..., filter_fn=top_k_pareto_filter(k=20)), ...])` |
104
+ | Adaptive search | Set up optuna study from scratch | `run_adaptive(world, scorer, factors, observables, n_trials=600)` |
105
+ | Reproducibility | Scattered scripts, no standard protocol | `Simulator` / `Scorer` protocols + `save_results()` / `load_results()` |
106
+
107
+ Existing tools solve pieces of this problem — [optuna](https://optuna.org/) for adaptive optimization, [pymoo](https://pymoo.org/) for multi-objective solvers, [SALib](https://salib.readthedocs.io/) for sensitivity analysis — but none provide the **hierarchical phase orchestration** that connects them into a single study.
108
+
109
+ ## Quick start
110
+
111
+ ```python
112
+ from trade_study import (
113
+ Direction,
114
+ Factor,
115
+ FactorType,
116
+ Observable,
117
+ Phase,
118
+ Study,
119
+ build_grid,
120
+ top_k_pareto_filter,
121
+ )
122
+
123
+ # 1. Define objectives
124
+ accuracy = Observable("accuracy", Direction.MAXIMIZE)
125
+ latency = Observable("latency_ms", Direction.MINIMIZE)
126
+ cost = Observable("cost_usd", Direction.MINIMIZE)
127
+
128
+ # 2. Define factors and build a design grid
129
+ factors = [
130
+ Factor("learning_rate", FactorType.CONTINUOUS, bounds=(1e-4, 1e-1)),
131
+ Factor("backend", FactorType.CATEGORICAL, levels=["A", "B", "C"]),
132
+ ]
133
+ grid = build_grid(factors, method="lhs", n_samples=500)
134
+
135
+ # 3. Run a hierarchical study
136
+ study = Study(
137
+ world=MySimulator(), # implements Simulator protocol
138
+ scorer=MyScorer(), # implements Scorer protocol
139
+ observables=[accuracy, latency, cost],
140
+ phases=[
141
+ Phase(
142
+ "screening",
143
+ grid=grid,
144
+ filter_fn=top_k_pareto_filter(k=20),
145
+ ),
146
+ Phase("benchmark", grid="carry", filter_fn=None),
147
+ ],
148
+ )
149
+ study.run(n_jobs=-1)
150
+
151
+ # 4. Inspect results
152
+ print(study.summary())
153
+ front = study.front() # non-dominated configs
154
+ hv = study.front_hypervolume(ref=...) # hypervolume indicator
155
+ ```
156
+
157
+ ### Protocols
158
+
159
+ Users implement two protocols to plug in their domain:
160
+
161
+ ```python
162
+ from trade_study import Scorer, Simulator
163
+
164
+
165
+ class MySimulator:
166
+ """Implements the Simulator protocol."""
167
+
168
+ def generate(self, config: dict) -> tuple:
169
+ """Return (truth, observations) for a given config."""
170
+ ...
171
+
172
+
173
+ class MyScorer:
174
+ """Implements the Scorer protocol."""
175
+
176
+ def score(self, truth, observations, config: dict) -> dict[str, float]:
177
+ """Return {observable_name: value} for a single trial."""
178
+ ...
179
+ ```
180
+
181
+ ## Installation
182
+
183
+ ```bash
184
+ pip install trade-study[all]
185
+ ```
186
+
187
+ Or install only the extras you need:
188
+
189
+ ```bash
190
+ pip install trade-study[design,pareto]
191
+ ```
192
+
193
+ | Extra | Packages | Purpose |
194
+ |-------|----------|---------|
195
+ | `design` | [pyDOE3](https://github.com/relf/pyDOE3), [SALib](https://github.com/SALib/SALib), [scipy](https://scipy.org/) | Grid construction and sensitivity screening |
196
+ | `pareto` | [pymoo](https://pymoo.org/) | Non-dominated sorting and indicators |
197
+ | `scoring` | [scoringrules](https://github.com/frazane/scoringrules) | Proper scoring rules (CRPS, WIS, etc.) |
198
+ | `stacking` | [arviz](https://github.com/arviz-devs/arviz), scipy | Bayesian and score-based ensemble weights |
199
+ | `adaptive` | [optuna](https://optuna.org/) | Multi-objective Bayesian optimization |
200
+ | `parallel` | joblib | Parallel grid execution |
201
+ | `all` | All of the above | |
202
+
203
+ **Core dependency**: numpy only.
204
+
205
+ ## API overview
206
+
207
+ ### Design
208
+
209
+ ```python
210
+ from trade_study import Factor, FactorType, build_grid, screen
211
+
212
+ factors = [
213
+ Factor("x", FactorType.CONTINUOUS, bounds=(0.0, 1.0)),
214
+ Factor("method", FactorType.CATEGORICAL, levels=["a", "b", "c"]),
215
+ ]
216
+
217
+ grid = build_grid(factors, method="sobol", n_samples=1024)
218
+ si = screen(run_fn, factors, method="morris", n_eval=3000)
219
+ ```
220
+
221
+ ### Execution
222
+
223
+ ```python
224
+ from trade_study import run_grid, run_adaptive
225
+
226
+ # Grid mode: evaluate all configs (optional parallelism)
227
+ results = run_grid(world, scorer, grid, observables, n_jobs=-1)
228
+
229
+ # Adaptive mode: multi-objective Bayesian optimization (NSGA-II)
230
+ results = run_adaptive(world, scorer, factors, observables, n_trials=600)
231
+ ```
232
+
233
+ ### Pareto analysis
234
+
235
+ ```python
236
+ from trade_study import extract_front, hypervolume, pareto_rank
237
+
238
+ front_mask = extract_front(results.scores, directions)
239
+ ranks = pareto_rank(results.scores, directions)
240
+ hv = hypervolume(results.scores[front_mask], directions, ref=ref_point)
241
+ ```
242
+
243
+ ### Stacking
244
+
245
+ ```python
246
+ from trade_study import stack_scores, stack_bayesian, ensemble_predict
247
+
248
+ weights = stack_scores(score_matrix) # simplex-constrained optimization
249
+ weights = stack_bayesian(idata_dict) # arviz stacking (Yao et al. 2018)
250
+ combined = ensemble_predict(predictions, weights)
251
+ ```
252
+
253
+ ### I/O
254
+
255
+ ```python
256
+ from trade_study import save_results, load_results
257
+
258
+ save_results(results, "study_results")
259
+ results = load_results("study_results")
260
+ ```
261
+
262
+ ## Related packages
263
+
264
+ | Package | Description |
265
+ |---------|-------------|
266
+ | [TradeStudy.jl](https://github.com/jcm-sci/TradeStudy.jl) | Julia implementation of the same framework |
267
+
268
+ ## Development
269
+
270
+ ```bash
271
+ uv sync --extra dev
272
+ just ci # lint → mypy --strict → pytest with coverage
273
+ just format # auto-format
274
+ just check # auto-fix lint
275
+ ```
276
+
277
+ ## License
278
+
279
+ MIT
@@ -0,0 +1,218 @@
1
+ # trade-study
2
+
3
+ [![CI](https://github.com/jcm-sci/trade-study/actions/workflows/ci.yml/badge.svg)](https://github.com/jcm-sci/trade-study/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
6
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
7
+ [![jcm-sci](https://img.shields.io/badge/jcm--sci-jcmacdonald.dev-blue)](https://jcmacdonald.dev/software/)
8
+
9
+ Multi-objective trade-study orchestration: define factors, build
10
+ parameter grids, run hierarchical study phases, and extract Pareto
11
+ fronts — for any domain where you compare alternatives against
12
+ competing objectives.
13
+
14
+ ## Statement of need
15
+
16
+ Comparing design alternatives against multiple objectives is a common
17
+ task across scientific simulation, engineering trade-offs, and ML
18
+ hyperparameter tuning. Researchers typically glue together separate
19
+ tools for grid construction, execution, scoring, and Pareto analysis,
20
+ writing ad-hoc scripts that are hard to reproduce or extend to
21
+ multi-phase studies (screening → refinement → benchmark).
22
+
23
+ `trade-study` provides a single orchestration layer that composes these
24
+ steps into a reproducible, protocol-driven workflow. Users supply a
25
+ `Simulator` (generates data) and a `Scorer` (evaluates it); the
26
+ framework handles grid construction, parallel execution, Pareto
27
+ extraction, and phase chaining. All components are modular and
28
+ optional — use only what you need.
29
+
30
+ This package targets researchers and practitioners who need:
31
+
32
+ - structured multi-phase experimental design (screen → refine → benchmark),
33
+ - multi-objective Pareto analysis across heterogeneous factors, and
34
+ - a reproducible Python API that separates domain logic from study orchestration.
35
+
36
+ ## Why trade-study?
37
+
38
+ | Need | Without trade-study | With trade-study |
39
+ |------|---------------------|------------------|
40
+ | Parameter grid | Manual `itertools.product` or one-off scripts | `build_grid(factors, method="sobol")` — full factorial, LHS, Sobol, Halton |
41
+ | Multi-objective ranking | Call pymoo directly, handle direction normalization | `extract_front(scores, directions)` — direction-aware |
42
+ | Phased studies | Custom loop with manual filtering between stages | `Study(phases=[Phase(..., filter_fn=top_k_pareto_filter(k=20)), ...])` |
43
+ | Adaptive search | Set up optuna study from scratch | `run_adaptive(world, scorer, factors, observables, n_trials=600)` |
44
+ | Reproducibility | Scattered scripts, no standard protocol | `Simulator` / `Scorer` protocols + `save_results()` / `load_results()` |
45
+
46
+ Existing tools solve pieces of this problem — [optuna](https://optuna.org/) for adaptive optimization, [pymoo](https://pymoo.org/) for multi-objective solvers, [SALib](https://salib.readthedocs.io/) for sensitivity analysis — but none provide the **hierarchical phase orchestration** that connects them into a single study.
47
+
48
+ ## Quick start
49
+
50
+ ```python
51
+ from trade_study import (
52
+ Direction,
53
+ Factor,
54
+ FactorType,
55
+ Observable,
56
+ Phase,
57
+ Study,
58
+ build_grid,
59
+ top_k_pareto_filter,
60
+ )
61
+
62
+ # 1. Define objectives
63
+ accuracy = Observable("accuracy", Direction.MAXIMIZE)
64
+ latency = Observable("latency_ms", Direction.MINIMIZE)
65
+ cost = Observable("cost_usd", Direction.MINIMIZE)
66
+
67
+ # 2. Define factors and build a design grid
68
+ factors = [
69
+ Factor("learning_rate", FactorType.CONTINUOUS, bounds=(1e-4, 1e-1)),
70
+ Factor("backend", FactorType.CATEGORICAL, levels=["A", "B", "C"]),
71
+ ]
72
+ grid = build_grid(factors, method="lhs", n_samples=500)
73
+
74
+ # 3. Run a hierarchical study
75
+ study = Study(
76
+ world=MySimulator(), # implements Simulator protocol
77
+ scorer=MyScorer(), # implements Scorer protocol
78
+ observables=[accuracy, latency, cost],
79
+ phases=[
80
+ Phase(
81
+ "screening",
82
+ grid=grid,
83
+ filter_fn=top_k_pareto_filter(k=20),
84
+ ),
85
+ Phase("benchmark", grid="carry", filter_fn=None),
86
+ ],
87
+ )
88
+ study.run(n_jobs=-1)
89
+
90
+ # 4. Inspect results
91
+ print(study.summary())
92
+ front = study.front() # non-dominated configs
93
+ hv = study.front_hypervolume(ref=...) # hypervolume indicator
94
+ ```
95
+
96
+ ### Protocols
97
+
98
+ Users implement two protocols to plug in their domain:
99
+
100
+ ```python
101
+ from trade_study import Scorer, Simulator
102
+
103
+
104
+ class MySimulator:
105
+ """Implements the Simulator protocol."""
106
+
107
+ def generate(self, config: dict) -> tuple:
108
+ """Return (truth, observations) for a given config."""
109
+ ...
110
+
111
+
112
+ class MyScorer:
113
+ """Implements the Scorer protocol."""
114
+
115
+ def score(self, truth, observations, config: dict) -> dict[str, float]:
116
+ """Return {observable_name: value} for a single trial."""
117
+ ...
118
+ ```
119
+
120
+ ## Installation
121
+
122
+ ```bash
123
+ pip install trade-study[all]
124
+ ```
125
+
126
+ Or install only the extras you need:
127
+
128
+ ```bash
129
+ pip install trade-study[design,pareto]
130
+ ```
131
+
132
+ | Extra | Packages | Purpose |
133
+ |-------|----------|---------|
134
+ | `design` | [pyDOE3](https://github.com/relf/pyDOE3), [SALib](https://github.com/SALib/SALib), [scipy](https://scipy.org/) | Grid construction and sensitivity screening |
135
+ | `pareto` | [pymoo](https://pymoo.org/) | Non-dominated sorting and indicators |
136
+ | `scoring` | [scoringrules](https://github.com/frazane/scoringrules) | Proper scoring rules (CRPS, WIS, etc.) |
137
+ | `stacking` | [arviz](https://github.com/arviz-devs/arviz), scipy | Bayesian and score-based ensemble weights |
138
+ | `adaptive` | [optuna](https://optuna.org/) | Multi-objective Bayesian optimization |
139
+ | `parallel` | joblib | Parallel grid execution |
140
+ | `all` | All of the above | |
141
+
142
+ **Core dependency**: numpy only.
143
+
144
+ ## API overview
145
+
146
+ ### Design
147
+
148
+ ```python
149
+ from trade_study import Factor, FactorType, build_grid, screen
150
+
151
+ factors = [
152
+ Factor("x", FactorType.CONTINUOUS, bounds=(0.0, 1.0)),
153
+ Factor("method", FactorType.CATEGORICAL, levels=["a", "b", "c"]),
154
+ ]
155
+
156
+ grid = build_grid(factors, method="sobol", n_samples=1024)
157
+ si = screen(run_fn, factors, method="morris", n_eval=3000)
158
+ ```
159
+
160
+ ### Execution
161
+
162
+ ```python
163
+ from trade_study import run_grid, run_adaptive
164
+
165
+ # Grid mode: evaluate all configs (optional parallelism)
166
+ results = run_grid(world, scorer, grid, observables, n_jobs=-1)
167
+
168
+ # Adaptive mode: multi-objective Bayesian optimization (NSGA-II)
169
+ results = run_adaptive(world, scorer, factors, observables, n_trials=600)
170
+ ```
171
+
172
+ ### Pareto analysis
173
+
174
+ ```python
175
+ from trade_study import extract_front, hypervolume, pareto_rank
176
+
177
+ front_mask = extract_front(results.scores, directions)
178
+ ranks = pareto_rank(results.scores, directions)
179
+ hv = hypervolume(results.scores[front_mask], directions, ref=ref_point)
180
+ ```
181
+
182
+ ### Stacking
183
+
184
+ ```python
185
+ from trade_study import stack_scores, stack_bayesian, ensemble_predict
186
+
187
+ weights = stack_scores(score_matrix) # simplex-constrained optimization
188
+ weights = stack_bayesian(idata_dict) # arviz stacking (Yao et al. 2018)
189
+ combined = ensemble_predict(predictions, weights)
190
+ ```
191
+
192
+ ### I/O
193
+
194
+ ```python
195
+ from trade_study import save_results, load_results
196
+
197
+ save_results(results, "study_results")
198
+ results = load_results("study_results")
199
+ ```
200
+
201
+ ## Related packages
202
+
203
+ | Package | Description |
204
+ |---------|-------------|
205
+ | [TradeStudy.jl](https://github.com/jcm-sci/TradeStudy.jl) | Julia implementation of the same framework |
206
+
207
+ ## Development
208
+
209
+ ```bash
210
+ uv sync --extra dev
211
+ just ci # lint → mypy --strict → pytest with coverage
212
+ just format # auto-format
213
+ just check # auto-fix lint
214
+ ```
215
+
216
+ ## License
217
+
218
+ MIT
@@ -0,0 +1,132 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "trade-study"
7
+ version = "0.1.0"
8
+ description = "Multi-objective trade-study orchestration: scoring, Pareto optimization, and Bayesian stacking"
9
+ authors = [
10
+ { name = "Joshua C. Macdonald", email = "jmacdo16@jh.edu" },
11
+ ]
12
+ readme = "README.md"
13
+ license = "MIT"
14
+ license-files = ["LICENSE"]
15
+ requires-python = ">=3.10"
16
+ keywords = [
17
+ "trade-study",
18
+ "model-selection",
19
+ "scoring-rules",
20
+ "pareto",
21
+ "bayesian-stacking",
22
+ "design-of-experiments",
23
+ "sensitivity-analysis",
24
+ "multi-objective-optimization",
25
+ ]
26
+ classifiers = [
27
+ "Development Status :: 3 - Alpha",
28
+ "Intended Audience :: Science/Research",
29
+ "Operating System :: OS Independent",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3 :: Only",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Topic :: Scientific/Engineering",
37
+ "Typing :: Typed",
38
+ ]
39
+ dependencies = [
40
+ "numpy>=1.24",
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/jcm-sci/trade-study"
45
+ Documentation = "https://jcm-sci.github.io/trade-study/"
46
+ Repository = "https://github.com/jcm-sci/trade-study"
47
+ Changelog = "https://github.com/jcm-sci/trade-study/blob/main/CHANGELOG.md"
48
+ Issues = "https://github.com/jcm-sci/trade-study/issues"
49
+
50
+ [project.optional-dependencies]
51
+ scoring = [
52
+ "scoringrules>=0.8",
53
+ ]
54
+ pareto = [
55
+ "pymoo>=0.6",
56
+ ]
57
+ stacking = [
58
+ "arviz>=0.20; python_version < '3.12'",
59
+ "arviz>=1.0; python_version >= '3.12'",
60
+ "scipy>=1.10",
61
+ ]
62
+ design = [
63
+ "pyDOE3>=1.5",
64
+ "SALib>=1.5",
65
+ "scipy>=1.10",
66
+ ]
67
+ adaptive = [
68
+ "optuna>=4.0",
69
+ ]
70
+ parallel = [
71
+ "joblib>=1.3",
72
+ ]
73
+ all = [
74
+ "trade-study[scoring,pareto,stacking,design,adaptive,parallel]",
75
+ ]
76
+ examples = [
77
+ "scikit-learn>=1.3",
78
+ "trade-study[all]",
79
+ ]
80
+ test = [
81
+ "pytest>=8.1",
82
+ "pytest-cov>=6.0",
83
+ "trade-study[all]",
84
+ ]
85
+ docs = [
86
+ "mkdocs-material>=9.5",
87
+ "mkdocstrings[python]>=0.27",
88
+ ]
89
+ dev = [
90
+ "mypy>=1.18",
91
+ "ruff>=0.13",
92
+ "trade-study[test,docs]",
93
+ ]
94
+
95
+ [tool.setuptools]
96
+ package-dir = {"" = "src"}
97
+
98
+ [tool.setuptools.packages.find]
99
+ where = ["src"]
100
+
101
+ [tool.ruff.format]
102
+ docstring-code-format = true
103
+ docstring-code-line-length = 72
104
+
105
+ [tool.ruff.lint]
106
+ select = ["ALL"]
107
+ ignore = [
108
+ "COM812",
109
+ "CPY001",
110
+ "D203",
111
+ "D212",
112
+ "PLC0415",
113
+ "PLR2004",
114
+ ]
115
+
116
+ [tool.ruff.lint.per-file-ignores]
117
+ "tests/**/*" = ["ANN201", "ANN401", "ARG002", "D103", "INP001", "PLR6301", "S101"]
118
+ "examples/**/*" = ["ANN401", "ARG002", "ERA001", "INP001", "PLR6301", "T201"]
119
+ "src/trade_study/protocols.py" = ["ANN401"]
120
+
121
+ [tool.ruff.lint.pylint]
122
+ max-args = 7
123
+
124
+ [tool.ruff.lint.pydocstyle]
125
+ convention = "google"
126
+
127
+ [tool.mypy]
128
+ strict = true
129
+
130
+ [tool.pytest.ini_options]
131
+ testpaths = ["tests"]
132
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+