strategy-genome 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 (47) hide show
  1. strategy_genome-0.1.0/.gitignore +9 -0
  2. strategy_genome-0.1.0/CHANGELOG.md +62 -0
  3. strategy_genome-0.1.0/LICENSE +21 -0
  4. strategy_genome-0.1.0/NOTICE +11 -0
  5. strategy_genome-0.1.0/PKG-INFO +163 -0
  6. strategy_genome-0.1.0/contrib/optunahub/strategy_genome/LICENSE +21 -0
  7. strategy_genome-0.1.0/docs/api.md +109 -0
  8. strategy_genome-0.1.0/docs/ecosystem.md +29 -0
  9. strategy_genome-0.1.0/docs/gp.md +52 -0
  10. strategy_genome-0.1.0/docs/index.md +122 -0
  11. strategy_genome-0.1.0/docs/optuna-and-freqtrade.md +123 -0
  12. strategy_genome-0.1.0/docs/provenance.md +71 -0
  13. strategy_genome-0.1.0/docs/trading-examples.md +36 -0
  14. strategy_genome-0.1.0/examples/freqtrade_strategy.py +55 -0
  15. strategy_genome-0.1.0/examples/gp_signal.py +25 -0
  16. strategy_genome-0.1.0/examples/multi_objective.py +48 -0
  17. strategy_genome-0.1.0/examples/optuna_study.py +26 -0
  18. strategy_genome-0.1.0/examples/quickstart.py +22 -0
  19. strategy_genome-0.1.0/examples/toy_backtest.py +9 -0
  20. strategy_genome-0.1.0/pyproject.toml +71 -0
  21. strategy_genome-0.1.0/src/strategy_genome/__init__.py +60 -0
  22. strategy_genome-0.1.0/src/strategy_genome/__main__.py +5 -0
  23. strategy_genome-0.1.0/src/strategy_genome/cli.py +92 -0
  24. strategy_genome-0.1.0/src/strategy_genome/evolution.py +751 -0
  25. strategy_genome-0.1.0/src/strategy_genome/fitness.py +145 -0
  26. strategy_genome-0.1.0/src/strategy_genome/genome.py +1076 -0
  27. strategy_genome-0.1.0/src/strategy_genome/gp/__init__.py +48 -0
  28. strategy_genome-0.1.0/src/strategy_genome/gp/expression_tree.py +749 -0
  29. strategy_genome-0.1.0/src/strategy_genome/gp/gp_engine.py +610 -0
  30. strategy_genome-0.1.0/src/strategy_genome/gp/gp_operators.py +323 -0
  31. strategy_genome-0.1.0/src/strategy_genome/gp/signal_validator.py +528 -0
  32. strategy_genome-0.1.0/src/strategy_genome/metrics.py +149 -0
  33. strategy_genome-0.1.0/src/strategy_genome/operators.py +827 -0
  34. strategy_genome-0.1.0/src/strategy_genome/optuna.py +300 -0
  35. strategy_genome-0.1.0/src/strategy_genome/pareto.py +159 -0
  36. strategy_genome-0.1.0/src/strategy_genome/population.py +769 -0
  37. strategy_genome-0.1.0/src/strategy_genome/py.typed +0 -0
  38. strategy_genome-0.1.0/src/strategy_genome/space.py +124 -0
  39. strategy_genome-0.1.0/src/strategy_genome/trading/__init__.py +71 -0
  40. strategy_genome-0.1.0/src/strategy_genome/trading/backtest.py +536 -0
  41. strategy_genome-0.1.0/src/strategy_genome/trading/coevolution.py +887 -0
  42. strategy_genome-0.1.0/src/strategy_genome/trading/meta_reward.py +845 -0
  43. strategy_genome-0.1.0/tests/test_core.py +292 -0
  44. strategy_genome-0.1.0/tests/test_gp.py +1204 -0
  45. strategy_genome-0.1.0/tests/test_metrics.py +98 -0
  46. strategy_genome-0.1.0/tests/test_optuna.py +166 -0
  47. strategy_genome-0.1.0/tests/test_trading.py +103 -0
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ checkpoints/
@@ -0,0 +1,62 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-11-15
8
+
9
+ First release. Carved out of [srfm-lab](https://github.com/Mattbusel/srfm-lab)
10
+ `ml/genetic` at commit `8cbfcb0` (MIT). See `docs/provenance.md`.
11
+
12
+ ### Added
13
+ - `evolve(fitness, space, population, generations, seed, ...)`: GA over a
14
+ user-supplied fitness function returning a float or a tuple of floats,
15
+ with `direction` / `directions` (maximize or minimize per objective).
16
+ - `strategy_genome.space`: `Float`, `Int`, `LogFloat`, `Categorical`, `Bool`
17
+ and shorthand dict spaces.
18
+ - `EvolutionResult` gains `best_params`, `best_value(s)`,
19
+ `diversity_history`, `best_fitness_history`, `n_failed_evaluations`,
20
+ `directions` and `to_dict()`.
21
+ - `CallableFitness`: failed or NaN evaluations score `-inf` and are counted
22
+ instead of aborting the run (opt out with `raise_errors=True`).
23
+ - `strategy_genome.optuna.GeneticStrategySampler`: steady-state GA as an
24
+ `optuna.samplers.BaseSampler` (single and multi-objective), plus
25
+ `space_to_distributions` / `distribution_to_param_range`. Works with
26
+ freqtrade hyperopt via `HyperOpt.generate_estimator`.
27
+ - `n_jobs` thread-pool evaluation; `callback` can stop a run early;
28
+ `initial` seeds generation 0; `multi_run` repeats a run over seeds.
29
+ - `GPEngine(terminal_names=..., function_names=...)`.
30
+ - `strategy-genome demo` CLI.
31
+ - Tests: numeric reference checks for metrics and hypervolume, GA
32
+ convergence and reproducibility, Optuna integration (tested on Optuna 3.4
33
+ and 5.0), trading examples, CLI; plus the 108 upstream GP tests.
34
+ - `contrib/optunahub/strategy_genome`: OptunaHub registry wrapper (not yet
35
+ submitted).
36
+
37
+ ### Changed
38
+ - Fitness is user-supplied. The hard-wired toy backtester, co-evolution and
39
+ meta-reward code moved to the opt-in `strategy_genome.trading` package.
40
+ - Return-series metrics moved to `strategy_genome.metrics`, Pareto indicators
41
+ to `strategy_genome.pareto`, GP to `strategy_genome.gp`.
42
+ - Checkpoint files are opt-in (`checkpoint_interval=0` by default) and
43
+ `verbose` defaults to `False`.
44
+ - Multi-objective parent selection uses NSGA-II crowded comparison.
45
+
46
+ ### Fixed (relative to srfm-lab)
47
+ - `StrategyGenome.clone()` was called but never defined, so the GA loop
48
+ raised `AttributeError`.
49
+ - Seeded runs were not reproducible (unseeded diversity sampler and operator
50
+ selection; genome ids drawn from the global RNG).
51
+ - `Population.__repr__` raised `ValueError`.
52
+ - Two-point crossover raised `IndexError` on single-gene chromosomes.
53
+ - `ParetoAnalysis.knee_point` returned an extreme point instead of the knee.
54
+ - `ParamRange.step` was ignored.
55
+ - Island mode did not advance the generation counter and left padded
56
+ members unevaluated.
57
+
58
+ ### Removed
59
+ - `visualization.py` (matplotlib dashboards) and the multiprocessing worker
60
+ that never ran in parallel.
61
+
62
+ [0.1.0]: https://github.com/Mattbusel/strategy-genome/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Busel
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,11 @@
1
+ strategy-genome
2
+ Copyright (c) 2026 Matthew Busel
3
+
4
+ This package was carved out of srfm-lab (https://github.com/Mattbusel/srfm-lab),
5
+ directory ml/genetic, at commit 8cbfcb0551d44e3ed1c20bdae9dc805d61521c62.
6
+ srfm-lab is released under the MIT License, copyright (c) 2026 Matthew Busel.
7
+
8
+ Most modules under src/strategy_genome/ (genome, population, operators,
9
+ evolution, gp/, trading/) are derived from that code; docs/provenance.md maps
10
+ each file to its origin and lists the changes made. tests/test_gp.py is the
11
+ upstream test suite with only its import paths changed.
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.5
2
+ Name: strategy-genome
3
+ Version: 0.1.0
4
+ Summary: Typed genetic-algorithm / genetic-programming optimizer (NSGA-II, island model, adaptive operators) with an Optuna sampler. Carved out of srfm-lab.
5
+ Project-URL: Homepage, https://github.com/Mattbusel/strategy-genome
6
+ Project-URL: Repository, https://github.com/Mattbusel/strategy-genome
7
+ Project-URL: Documentation, https://github.com/Mattbusel/strategy-genome/tree/main/docs
8
+ Project-URL: Changelog, https://github.com/Mattbusel/strategy-genome/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/Mattbusel/strategy-genome/issues
10
+ Project-URL: Upstream (srfm-lab), https://github.com/Mattbusel/srfm-lab
11
+ Author: Matthew Busel
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ License-File: NOTICE
15
+ Keywords: evolutionary-algorithm,freqtrade,genetic-algorithm,genetic-programming,hyperopt,hyperparameter-optimization,multi-objective-optimization,nsga-ii,optuna,quantitative-finance,sampler,trading-strategy
16
+ Classifier: Development Status :: 4 - Beta
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Financial and Insurance Industry
19
+ Classifier: Intended Audience :: Science/Research
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Topic :: Office/Business :: Financial :: Investment
28
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
29
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
30
+ Classifier: Typing :: Typed
31
+ Requires-Python: >=3.10
32
+ Requires-Dist: numpy>=1.22
33
+ Provides-Extra: optuna
34
+ Requires-Dist: optuna>=3.4; extra == 'optuna'
35
+ Provides-Extra: scipy
36
+ Requires-Dist: scipy>=1.8; extra == 'scipy'
37
+ Provides-Extra: test
38
+ Requires-Dist: optuna>=3.4; extra == 'test'
39
+ Requires-Dist: pytest>=7; extra == 'test'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # strategy-genome
43
+
44
+ A typed genetic algorithm (GA) and genetic programming (GP) optimizer for
45
+ Python, with an Optuna sampler so any Optuna study can use it.
46
+
47
+ - **You bring the fitness function.** `evolve(fitness, space)` calls
48
+ `fitness(genome)` and expects a float, or a tuple of floats for
49
+ multi-objective runs. Nothing about trading is built in.
50
+ - **Mixed search spaces:** floats (optionally on a step grid), integers,
51
+ log-scale floats, categoricals and booleans.
52
+ - **Real GA machinery:** elitism, tournament selection, SBX / arithmetic /
53
+ uniform / one- and two-point crossover, gaussian / polynomial / creep /
54
+ boundary mutation, adaptive mutation rate and adaptive operator choice,
55
+ NSGA-II for multiple objectives, an island model, stagnation restarts, and a
56
+ hall of fame.
57
+ - **Reproducible:** the same seed and the same fitness give the same run.
58
+ - **Light:** the GA core is pure standard-library Python. numpy is used by
59
+ the GP submodule and the trading examples. Optuna is an optional extra.
60
+
61
+ Carved out of [srfm-lab](https://github.com/Mattbusel/srfm-lab)
62
+ (`ml/genetic`), MIT licensed. See [provenance](https://github.com/Mattbusel/strategy-genome/blob/main/docs/provenance.md) for what was
63
+ changed and which upstream bugs were fixed on the way out.
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ pip install strategy-genome # GA core + GP (numpy)
69
+ pip install "strategy-genome[optuna]" # adds the Optuna sampler
70
+ ```
71
+
72
+ Python 3.10 or newer.
73
+
74
+ ## Quickstart
75
+
76
+ ```python
77
+ from strategy_genome import evolve, space as sp
78
+
79
+ def fitness(g):
80
+ # g["name"] reads one parameter; g.params is the whole dict
81
+ return (g["x"] - 3.0) ** 2 + (g["y"] + 1.0) ** 2
82
+
83
+ result = evolve(
84
+ fitness,
85
+ {"x": sp.Float(-10, 10), "y": sp.Float(-10, 10)},
86
+ population=40,
87
+ generations=80,
88
+ seed=7,
89
+ direction="minimize",
90
+ )
91
+
92
+ result.best_params # {'x': 3.00..., 'y': -0.99...}
93
+ result.best_value # what your function returned for the best genome
94
+ result.hall_of_fame # best unique genomes seen, best first
95
+ result.diversity_history # mean pairwise parameter distance per generation
96
+ ```
97
+
98
+ ### Search spaces
99
+
100
+ ```python
101
+ space = {
102
+ "fast": sp.Int(2, 50),
103
+ "slow": sp.Int(10, 200, step=5),
104
+ "stop": sp.Float(0.005, 0.10),
105
+ "lr": sp.LogFloat(1e-5, 1e-1),
106
+ "kind": sp.Categorical(["ema", "sma", "wma"]),
107
+ "short": sp.Bool(),
108
+ }
109
+ ```
110
+
111
+ Shorthand also works: `(0, 10)` is an integer range, `(0.0, 1.0)` a float
112
+ range, and a list is a categorical.
113
+
114
+ ### Several objectives
115
+
116
+ Return a tuple and give one direction per objective. The run switches to
117
+ NSGA-II and `result.pareto_front` holds the non-dominated genomes.
118
+
119
+ ```python
120
+ def fitness(g):
121
+ returns = backtest(g.params)
122
+ return sharpe(returns), max_drawdown(returns)
123
+
124
+ result = evolve(fitness, space, population=60, generations=100, seed=1,
125
+ directions=["maximize", "minimize"])
126
+ for g in result.pareto_front:
127
+ print(g.values, g.params)
128
+ ```
129
+
130
+ ### Failures
131
+
132
+ If `fitness` raises or returns NaN, that genome scores `-inf` and the run
133
+ continues. `result.n_failed_evaluations` counts them. Pass
134
+ `raise_errors=True` to stop on the first error instead.
135
+
136
+ ## Use it from Optuna (and freqtrade)
137
+
138
+ ```python
139
+ import optuna
140
+ from strategy_genome.optuna import GeneticStrategySampler
141
+
142
+ study = optuna.create_study(sampler=GeneticStrategySampler(population_size=30, seed=1))
143
+ study.optimize(objective, n_trials=500)
144
+ ```
145
+
146
+ freqtrade's hyperopt runs on Optuna and lets a strategy return any
147
+ `optuna.samplers.BaseSampler` from `HyperOpt.generate_estimator`, so the
148
+ sampler drops straight in. See [Optuna and freqtrade](https://github.com/Mattbusel/strategy-genome/blob/main/docs/optuna-and-freqtrade.md),
149
+ including a benchmark against Random, NSGA-II and TPE.
150
+
151
+ ## More
152
+
153
+ - [API reference](https://github.com/Mattbusel/strategy-genome/blob/main/docs/api.md)
154
+ - [Optuna and freqtrade](https://github.com/Mattbusel/strategy-genome/blob/main/docs/optuna-and-freqtrade.md)
155
+ - [Genetic programming (`strategy_genome.gp`)](https://github.com/Mattbusel/strategy-genome/blob/main/docs/gp.md)
156
+ - [Trading examples (`strategy_genome.trading`)](https://github.com/Mattbusel/strategy-genome/blob/main/docs/trading-examples.md)
157
+ - [Provenance and upstream fixes](https://github.com/Mattbusel/strategy-genome/blob/main/docs/provenance.md)
158
+ - [Ecosystem plan (OptunaHub)](https://github.com/Mattbusel/strategy-genome/blob/main/docs/ecosystem.md)
159
+ - Examples: [`examples/`](https://github.com/Mattbusel/strategy-genome/tree/main/examples)
160
+
161
+ ## License
162
+
163
+ MIT. Copyright (c) 2026 Matthew Busel. See `LICENSE` and `NOTICE`.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Busel
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,109 @@
1
+ # API reference
2
+
3
+ Everything below is importable from `strategy_genome` unless a submodule is
4
+ named.
5
+
6
+ ## `evolve`
7
+
8
+ ```python
9
+ evolve(
10
+ fitness, # fn(genome) -> float | tuple[float, ...]
11
+ space, # {name: ParamRange} | shorthand dict | [ParamRange]
12
+ population=50,
13
+ generations=100,
14
+ seed=None,
15
+ *,
16
+ direction=None, # "maximize" (default) | "minimize"
17
+ directions=None, # one per objective, for multi-objective runs
18
+ initial=None, # list of param dicts to seed generation 0
19
+ n_jobs=1, # threads for fitness evaluation
20
+ callback=None, # callback(gen, population, stats); return False to stop
21
+ raise_errors=False,
22
+ verbose=False,
23
+ **config, # any EvolutionConfig field
24
+ ) -> EvolutionResult
25
+ ```
26
+
27
+ Useful `**config` fields (see `EvolutionConfig` for all of them):
28
+
29
+ | field | default | meaning |
30
+ |---|---|---|
31
+ | `elite_fraction` | 0.10 | share of the population copied unchanged each generation |
32
+ | `tournament_size` | 5 | tournament size for parent selection |
33
+ | `crossover_rate` | 0.80 | starting crossover probability (cosine-annealed) |
34
+ | `initial_mutation_rate` | 0.10 | starting per-gene mutation probability |
35
+ | `use_adaptive_mutation` | True | adapt the mutation rate to progress and diversity |
36
+ | `use_adaptive_operators` | True | sample crossover/mutation operators by weight |
37
+ | `mutation_method` / `crossover_method` | gaussian / uniform | used when adaptive operators are off |
38
+ | `use_island_model`, `n_islands`, `migration_interval` | False, 4, 20 | island model (single objective) |
39
+ | `stagnation_patience`, `max_restarts`, `restart_method` | 30, 5, partial | restarts when progress stalls |
40
+ | `convergence_patience` | 50 | generations without improvement before stopping |
41
+ | `use_fitness_sharing`, `sharing_sigma` | False, 0.15 | niching |
42
+ | `checkpoint_interval`, `checkpoint_dir` | 0, ./checkpoints | write best-genome JSON every N generations (0 = off) |
43
+
44
+ ## `EvolutionResult`
45
+
46
+ | attribute | meaning |
47
+ |---|---|
48
+ | `best_genome` | best genome seen (hall-of-fame head) |
49
+ | `best_params` | its parameters as a dict |
50
+ | `best_value` / `best_values` | what your fitness returned for it |
51
+ | `hall_of_fame` | best unique genomes, best first |
52
+ | `pareto_front` | non-dominated genomes (multi-objective runs) |
53
+ | `stats_history` | `PopulationStats` per generation |
54
+ | `diversity_history` | mean pairwise distance in normalized parameter space, per generation |
55
+ | `best_fitness_history` | best internal fitness per generation |
56
+ | `n_generations_run`, `n_evaluations`, `n_failed_evaluations`, `n_restarts` | counters |
57
+ | `converged`, `convergence_reason` | why the run stopped |
58
+ | `directions` | the direction of each objective |
59
+ | `to_dict()` | JSON-friendly summary |
60
+ | `summary()` | printable summary |
61
+
62
+ Internally every objective is maximized: `genome.fitness` and
63
+ `genome.objectives` are sign-adjusted. `genome.value` / `genome.values` are
64
+ always your function's raw output.
65
+
66
+ ## Genomes
67
+
68
+ `Genome` (alias of `StrategyGenome`) is what your fitness function receives.
69
+
70
+ - `g["name"]`, `g.get("name", default)`, `g.params`
71
+ - `g.value`, `g.values`, `g.fitness`, `g.objectives`
72
+ - `g.clone()`, `g.mutate(rate, method=...)`, `StrategyGenome.crossover(a, b, method=...)`
73
+ - `g.dominates(other)` (Pareto dominance on `objectives`)
74
+
75
+ `GenomeFactory.from_space(space, seed=...)` builds random, default or
76
+ dict-specified genomes and serializes them to JSON-compatible dicts.
77
+
78
+ ## Search space (`strategy_genome.space`)
79
+
80
+ `Float(low, high, step=None)`, `Int(low, high, step=1)`,
81
+ `LogFloat(low, high)`, `Categorical(choices)`, `Bool()`, and
82
+ `normalize_space(space)` which turns any accepted form into a list of named
83
+ `ParamRange`.
84
+
85
+ ## Lower-level pieces
86
+
87
+ - `GeneticAlgorithm(config, factory, evaluator)`: the loop itself. The
88
+ evaluator is any object with `evaluate(genome) -> genome` that sets
89
+ `genome.fitness` (higher is better). `CallableFitness` wraps a function.
90
+ - `Population`, `HallOfFame`, `SelectionOperator` (tournament, roulette,
91
+ rank, SUS, lexicase), `FitnessSharing`, `DiversityMetrics`.
92
+ - `MutationOperator`, `CrossoverOperator`: the operators, usable on their own.
93
+ - `AdaptiveMutationRate`, `AdaptiveOperatorSelection`, `NichePreservation`
94
+ (deterministic crowding, clearing, restricted mating), `IslandModel`,
95
+ `RestartStrategy`.
96
+ - `ParetoAnalysis`: hypervolume (exact in 2-D, Monte Carlo above), spacing,
97
+ knee point.
98
+ - `metrics`: `compute_sharpe`, `compute_sortino`, `compute_calmar`,
99
+ `compute_max_drawdown`, `compute_profit_factor`, `compute_win_rate`,
100
+ `compute_omega_ratio`, `compute_var`, `compute_cvar`,
101
+ `compute_information_ratio`. Ratios with a zero denominator return capped
102
+ sentinels (for example 10.0) rather than infinity, as upstream did.
103
+ - `multi_run(fitness, space, n_runs=5, ...)`: repeat a run over seeds to see
104
+ whether a result is robust or a lucky seed.
105
+
106
+ ## CLI
107
+
108
+ `strategy-genome demo [--strategy momentum] [--prices prices.csv] [--json out.json]`
109
+ runs the GA against the toy backtester so you can watch it work.
@@ -0,0 +1,29 @@
1
+ # Ecosystem plan
2
+
3
+ Where this package can plug into existing tools, and the status of each.
4
+
5
+ ## freqtrade: no merge needed
6
+
7
+ freqtrade hyperopt runs on Optuna and accepts any `BaseSampler` instance
8
+ from a strategy's `HyperOpt.generate_estimator`. The integration is a
9
+ user-side snippet, documented in [Optuna and freqtrade](optuna-and-freqtrade.md)
10
+ and `examples/freqtrade_strategy.py`. Verified against freqtrade's
11
+ `docs/advanced-hyperopt.md` and `freqtrade/optimize/hyperopt/hyperopt_optimizer.py`
12
+ on `develop` (release 2026.10).
13
+
14
+ ## OptunaHub sampler: prepared, not submitted
15
+
16
+ OptunaHub (`optuna/optunahub-registry`) hosts community samplers under
17
+ `package/samplers/<name>/`. A package there is a thin wrapper that imports
18
+ this library. `contrib/optunahub/strategy_genome/` holds the wrapper
19
+ (`__init__.py`, `_sampler.py`, `example.py`, `LICENSE`, `requirements.txt`), matching the layout of existing registry packages such as `auto_sampler`.
20
+
21
+ Not submitted yet: the registry requires a `README.md` inside the package
22
+ directory, and that file is not written here.
23
+
24
+ ## OptunaHub benchmark: proposed
25
+
26
+ OptunaHub's benchmark list has no financial problem. A walk-forward
27
+ strategy-overfitting benchmark (optimize on one window, score on the next,
28
+ report the gap) would be a new category. It would be built on this package
29
+ and a walk-forward splitter, and has the same `README.md` requirement.
@@ -0,0 +1,52 @@
1
+ # Genetic programming: `strategy_genome.gp`
2
+
3
+ An opt-in submodule that evolves symbolic expression trees, for example
4
+ candidate alpha signals built from feature arrays. Needs numpy. scipy is used
5
+ for Spearman rank correlation when installed, with a numpy fallback.
6
+
7
+ ```python
8
+ import numpy as np
9
+ from strategy_genome.gp import GPConfig, GPEngine, SignalValidator
10
+
11
+ rng = np.random.default_rng(0)
12
+ n = 500
13
+ data = {
14
+ "close": rng.standard_normal(n).cumsum() + 100.0,
15
+ "volume": rng.uniform(1e5, 1e7, n),
16
+ "mom_5d": rng.standard_normal(n),
17
+ "mom_20d": rng.standard_normal(n),
18
+ }
19
+ forward_returns = rng.standard_normal(n) * 0.01
20
+
21
+ engine = GPEngine(seed=1, terminal_names=list(data))
22
+ pareto = engine.run(data, forward_returns,
23
+ GPConfig(population_size=200, max_generations=20, verbosity=0))
24
+
25
+ best = pareto[0]
26
+ print(engine.export_best(best)["expression"], best.fitness_scores)
27
+ ```
28
+
29
+ What is inside:
30
+
31
+ - `ExpressionTree`, `ExpressionNode`, `TreeGenerator`: trees over unary and
32
+ binary functions (arithmetic with protected division, EMA, rolling
33
+ statistics, ranks, lags and more; see `ALL_FUNCTION_NAMES`), simplification,
34
+ depth and size introspection.
35
+ - `gp_operators`: subtree crossover, point / subtree / hoist mutation,
36
+ tournament, fitness-proportional and lexicase selection.
37
+ - `GPEngine`: ramped half-and-half initialization, tournament + lexicase
38
+ parent selection, elitism, parsimony pressure, early stopping, and a Pareto
39
+ front over information coefficient (IC) and ICIR.
40
+ - `SignalValidator`: IC and ICIR checks, a look-ahead test, stability across
41
+ sub-periods, correlation against an existing signal library, and
42
+ out-of-sample validation.
43
+
44
+ Notes:
45
+
46
+ - `terminal_names` defaults to the srfm-lab signal library
47
+ (`ALL_TERMINALS`). Pass the keys of your own data dict, as above, or trees
48
+ will reference names you do not have (those evaluate to 0).
49
+ - `GPEngine(seed=...)` seeds Python's and numpy's global RNGs, as upstream
50
+ did.
51
+ - The 108 upstream tests for this subsystem ship unchanged in
52
+ `tests/test_gp.py` and all pass.
@@ -0,0 +1,122 @@
1
+ # strategy-genome
2
+
3
+ A typed genetic algorithm (GA) and genetic programming (GP) optimizer for
4
+ Python, with an Optuna sampler so any Optuna study can use it.
5
+
6
+ - **You bring the fitness function.** `evolve(fitness, space)` calls
7
+ `fitness(genome)` and expects a float, or a tuple of floats for
8
+ multi-objective runs. Nothing about trading is built in.
9
+ - **Mixed search spaces:** floats (optionally on a step grid), integers,
10
+ log-scale floats, categoricals and booleans.
11
+ - **Real GA machinery:** elitism, tournament selection, SBX / arithmetic /
12
+ uniform / one- and two-point crossover, gaussian / polynomial / creep /
13
+ boundary mutation, adaptive mutation rate and adaptive operator choice,
14
+ NSGA-II for multiple objectives, an island model, stagnation restarts, and a
15
+ hall of fame.
16
+ - **Reproducible:** the same seed and the same fitness give the same run.
17
+ - **Light:** the GA core is pure standard-library Python. numpy is used by
18
+ the GP submodule and the trading examples. Optuna is an optional extra.
19
+
20
+ Carved out of [srfm-lab](https://github.com/Mattbusel/srfm-lab)
21
+ (`ml/genetic`), MIT licensed. See [provenance](https://github.com/Mattbusel/strategy-genome/blob/main/docs/provenance.md) for what was
22
+ changed and which upstream bugs were fixed on the way out.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install strategy-genome # GA core + GP (numpy)
28
+ pip install "strategy-genome[optuna]" # adds the Optuna sampler
29
+ ```
30
+
31
+ Python 3.10 or newer.
32
+
33
+ ## Quickstart
34
+
35
+ ```python
36
+ from strategy_genome import evolve, space as sp
37
+
38
+ def fitness(g):
39
+ # g["name"] reads one parameter; g.params is the whole dict
40
+ return (g["x"] - 3.0) ** 2 + (g["y"] + 1.0) ** 2
41
+
42
+ result = evolve(
43
+ fitness,
44
+ {"x": sp.Float(-10, 10), "y": sp.Float(-10, 10)},
45
+ population=40,
46
+ generations=80,
47
+ seed=7,
48
+ direction="minimize",
49
+ )
50
+
51
+ result.best_params # {'x': 3.00..., 'y': -0.99...}
52
+ result.best_value # what your function returned for the best genome
53
+ result.hall_of_fame # best unique genomes seen, best first
54
+ result.diversity_history # mean pairwise parameter distance per generation
55
+ ```
56
+
57
+ ### Search spaces
58
+
59
+ ```python
60
+ space = {
61
+ "fast": sp.Int(2, 50),
62
+ "slow": sp.Int(10, 200, step=5),
63
+ "stop": sp.Float(0.005, 0.10),
64
+ "lr": sp.LogFloat(1e-5, 1e-1),
65
+ "kind": sp.Categorical(["ema", "sma", "wma"]),
66
+ "short": sp.Bool(),
67
+ }
68
+ ```
69
+
70
+ Shorthand also works: `(0, 10)` is an integer range, `(0.0, 1.0)` a float
71
+ range, and a list is a categorical.
72
+
73
+ ### Several objectives
74
+
75
+ Return a tuple and give one direction per objective. The run switches to
76
+ NSGA-II and `result.pareto_front` holds the non-dominated genomes.
77
+
78
+ ```python
79
+ def fitness(g):
80
+ returns = backtest(g.params)
81
+ return sharpe(returns), max_drawdown(returns)
82
+
83
+ result = evolve(fitness, space, population=60, generations=100, seed=1,
84
+ directions=["maximize", "minimize"])
85
+ for g in result.pareto_front:
86
+ print(g.values, g.params)
87
+ ```
88
+
89
+ ### Failures
90
+
91
+ If `fitness` raises or returns NaN, that genome scores `-inf` and the run
92
+ continues. `result.n_failed_evaluations` counts them. Pass
93
+ `raise_errors=True` to stop on the first error instead.
94
+
95
+ ## Use it from Optuna (and freqtrade)
96
+
97
+ ```python
98
+ import optuna
99
+ from strategy_genome.optuna import GeneticStrategySampler
100
+
101
+ study = optuna.create_study(sampler=GeneticStrategySampler(population_size=30, seed=1))
102
+ study.optimize(objective, n_trials=500)
103
+ ```
104
+
105
+ freqtrade's hyperopt runs on Optuna and lets a strategy return any
106
+ `optuna.samplers.BaseSampler` from `HyperOpt.generate_estimator`, so the
107
+ sampler drops straight in. See [Optuna and freqtrade](https://github.com/Mattbusel/strategy-genome/blob/main/docs/optuna-and-freqtrade.md),
108
+ including a benchmark against Random, NSGA-II and TPE.
109
+
110
+ ## More
111
+
112
+ - [API reference](https://github.com/Mattbusel/strategy-genome/blob/main/docs/api.md)
113
+ - [Optuna and freqtrade](https://github.com/Mattbusel/strategy-genome/blob/main/docs/optuna-and-freqtrade.md)
114
+ - [Genetic programming (`strategy_genome.gp`)](https://github.com/Mattbusel/strategy-genome/blob/main/docs/gp.md)
115
+ - [Trading examples (`strategy_genome.trading`)](https://github.com/Mattbusel/strategy-genome/blob/main/docs/trading-examples.md)
116
+ - [Provenance and upstream fixes](https://github.com/Mattbusel/strategy-genome/blob/main/docs/provenance.md)
117
+ - [Ecosystem plan (OptunaHub)](https://github.com/Mattbusel/strategy-genome/blob/main/docs/ecosystem.md)
118
+ - Examples: [`examples/`](https://github.com/Mattbusel/strategy-genome/tree/main/examples)
119
+
120
+ ## License
121
+
122
+ MIT. Copyright (c) 2026 Matthew Busel. See `LICENSE` and `NOTICE`.