Evolutionary Optimizer
Grid search is exponential. Random search is wasteful. Evolutionary optimization finds good strategy parameters by mimicking natural selection: the fittest parameter sets survive, mutate, and recombine. Horizon’s evolution engine draws from the Darwin SDK and the ProFiT framework (Siper et al., “Program Search for Financial Trading”) to deliver parameter optimization with 6 selection methods, NSGA-II multi-objective optimization, island model parallelism, and adaptive mutation. All genetic operators run in Rust; the fitness function callshz.backtest() in Python.
Overview
6 Selection Methods
Tournament, Uniform, Roulette Wheel, Rank, UCB1, and Epsilon-Greedy.
NSGA-II Multi-Objective
Pareto fronts, crowding distance, and non-dominated sorting for multi-objective optimization.
Island Model
Parallel populations with Ring and FullyConnected migration topologies.
Adaptive Mutation
Rechenberg’s 1/5 success rule for self-tuning mutation rates.
Convergence Detection
Early stopping via patience exhaustion and diversity collapse detection.
Walk-Forward Validation
Train/validate split prevents overfitting to in-sample data.
Selection Methods (Rust)
All selection methods are deterministic given a seed and run in Rust.hz.tournament_select
Select parent indices using tournament selection. Picktournament_size individuals at random; the one with the highest fitness wins.
hz.roulette_select
Probability proportional to fitness. Handles negative fitnesses by shifting the minimum to a small positive value.hz.rank_select
Probability proportional to rank position. Rank 1 (worst) gets weight 1, rank N (best) gets weight N. Insensitive to fitness magnitude.hz.ucb1_select
Upper Confidence Bound 1. Balances exploitation (high fitness) with exploration (rarely selected individuals). From Auer et al. (2002), used in the ProFiT framework for parent selection.| Parameter | Type | Description |
|---|---|---|
fitnesses | list[float] | Fitness values per individual |
selection_counts | list[int] | Times each individual has been selected |
total_selections | int | Total selections across all individuals |
count | int | Number of selections to make |
hz.epsilon_greedy_select
With probability epsilon pick a random individual; otherwise pick the best. Simple exploration-exploitation tradeoff.hz.uniform_select
Pure random selection with equal probability. Useful as a baseline or for maximum exploration.Genetic Operators (Rust)
hz.gaussian_mutate
Apply bounded Gaussian noise to a genome. Discrete parameters are rounded after mutation.hz.uniform_crossover
Recombine genes from two parents. Each gene has probabilityrate of coming from parent B.
hz.init_population
Generate a random initial population within bounds.NSGA-II Multi-Objective Optimization
Multi-objective optimization using Non-dominated Sorting Genetic Algorithm II. Optimizes multiple objectives simultaneously (e.g., maximize Sharpe AND minimize drawdown) without collapsing them into a single scalar. Returns the Pareto front of non-dominated solutions. Based on Deb et al., “A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II” (2002).hz.non_dominated_sort
Fast non-dominated sorting. Returns fronts wherefronts[0] is the Pareto front.
hz.crowding_distance_assign
Compute crowding distance for individuals in the same front. Boundary individuals get infinite distance; interior individuals get distance proportional to objective-space gaps.hz.nsga2_select
Select N individuals using NSGA-II ranking (front rank + crowding distance).hz.pareto_front
Extract the Pareto front (non-dominated set) indices.Island Model
Multiple populations evolve in parallel using different selection methods, with periodic migration of top individuals between islands. Each island uses a different selection strategy for diversity, following the ProFiT framework’s approach of per-island specialization.hz.island_migrate
Perform migration between islands based on the configured topology.hz.island_selection_methods
Get the default selection method assignment per island. Methods cycle through Roulette, Rank, UCB1, and Epsilon-Greedy.Convergence Detection
hz.check_convergence
Detect when evolution has stalled (no improvement forpatience generations) or the population has collapsed (diversity below threshold).
Adaptive Mutation
hz.adapt_mutation_sigma
Self-tune mutation sigma using Rechenberg’s 1/5 success rule. If the acceptance rate exceeds the target (default 20%), increase sigma to explore more broadly. If below, decrease sigma to fine-tune. Also used in the Continuous Program Search framework (Siper et al.) for adapting mutation strength.Population Analytics
hz.population_diversity
Mean pairwise L2 distance between genomes, normalized by dimension. Higher values indicate more diverse populations.hz.compute_generation_stats
Per-generation statistics: best, median, mean, worst, std, and diversity.Types
SelectionMethod
MigrationTopology
IslandConfig
EvolutionConfig, Individual, ParamBound, EvolutionResult
Python Orchestration
hz.evolve
Full evolutionary optimization loop with walk-forward validation.| Parameter | Type | Default | Description |
|---|---|---|---|
data | Any | required | Historical data for backtesting |
pipeline_factory | Callable | required | Function(params) to pipeline |
param_bounds | list[dict] | required | Parameter bounds |
pop_size | int | 50 | Population size |
generations | int | 100 | Maximum generations |
selection | str | "tournament" | Selection method |
early_stopping | bool | True | Enable convergence detection |
patience | int | 20 | Generations without improvement before stopping |
adaptive_mutation | bool | False | Enable Rechenberg’s 1/5 rule |
epsilon | float | 0.1 | Epsilon for epsilon-greedy selection |
fraction_train | float | 0.7 | Train/validate split |
fitness_fn | Callable | None | Custom fitness(result) to float |
hz.evolve_nsga2
Multi-objective evolution using NSGA-II. Returns the Pareto front.| Parameter | Type | Default | Description |
|---|---|---|---|
objectives | list[dict] | required | Objectives to optimize |
pop_size | int | 50 | Population size |
generations | int | 100 | Maximum generations |
early_stopping | bool | True | Enable convergence detection |
hz.evolve_islands
Island model evolution with parallel populations and periodic migration.| Parameter | Type | Default | Description |
|---|---|---|---|
n_islands | int | 4 | Number of parallel populations |
migration_interval | int | 5 | Generations between migrations |
migration_count | int | 2 | Top individuals per migration |
topology | str | "ring" | "ring" or "fully_connected" |
Examples
Optimize Market Making Parameters
Multi-Objective: Sharpe vs Drawdown
Island Model with Diverse Selection
Mathematical Background
Tournament Selection
Tournament Selection
For each selection, pick
tournament_size individuals at random (with replacement). The one with the highest fitness wins. Larger tournaments increase selection pressure (faster convergence but less diversity).UCB1 Selection (Auer et al., 2002)
UCB1 Selection (Auer et al., 2002)
Treats each individual as a multi-armed bandit arm. Selects the individual that maximizes:
UCB1(i) = fitness(i) + sqrt(2 * ln(T) / n_i)where T is the total number of selections and n_i is the number of times individual i has been selected. The first term exploits known-good individuals; the second term explores rarely-selected ones. This method was adopted by the ProFiT framework (Siper et al.) for parent selection in evolutionary strategy discovery.Roulette Wheel Selection
Roulette Wheel Selection
Probability of selecting individual i is proportional to its fitness. Fitnesses are shifted so the minimum becomes a small positive value, ensuring valid probabilities even with negative fitnesses:
p(i) = (f(i) - f_min + epsilon) / sum.Rank Selection
Rank Selection
Individuals are sorted by fitness and assigned ranks 1 (worst) through N (best). Selection probability is proportional to rank:
p(i) = rank(i) / (N*(N+1)/2). Unlike roulette wheel, this is insensitive to fitness magnitudes, preventing a single dominant individual from monopolizing selection.NSGA-II (Deb et al., 2002)
NSGA-II (Deb et al., 2002)
Non-dominated Sorting Genetic Algorithm II for multi-objective optimization:
- Non-dominated sorting: Partition population into fronts. Front 0 (Pareto front) contains solutions where no other solution is better on all objectives. Front 1 is dominated only by front 0, etc.
- Crowding distance: Within each front, measure the density of nearby solutions in objective space. Boundary solutions get infinite distance. This preserves diversity along the Pareto front.
- Selection: Prefer lower-rank fronts. Within the same front, prefer higher crowding distance (less crowded regions).
Island Model
Island Model
Multiple populations (islands) evolve independently with different selection methods. Periodically, top individuals migrate between islands:
- Ring topology: Island i sends to island (i+1) mod N
- Fully connected: Every island sends to every other island
Rechenberg's 1/5 Rule (Adaptive Mutation)
Rechenberg's 1/5 Rule (Adaptive Mutation)
Track the fraction of mutations that produce offspring fitter than their parents. If the success rate exceeds 1/5 (20%), increase mutation strength (sigma) to explore more broadly. If below, decrease sigma to fine-tune near optima.
sigma_new = sigma * c if success_rate is greater than 0.2sigma_new = sigma / c if success_rate is less than 0.2where c is the adaptation factor (default 1.5). Sigma is clamped to [sigma_min, sigma_max].This self-tuning mechanism was also adopted by the Continuous Program Search framework (Siper et al.) for adapting mutation strength based on acceptance rates.Convergence Detection
Convergence Detection
Evolution stops early when:
- Patience exhausted: No improvement exceeding
min_improvementforpatienceconsecutive generations. - Diversity collapse: Population diversity (mean pairwise L2 distance) falls below
diversity_threshold, indicating premature convergence.
Walk-Forward Validation
Walk-Forward Validation
Data is split into train (first
fraction_train) and validate (rest). Evolution optimizes fitness on the training set. The final reported fitness is evaluated on the held-out validation set, preventing overfitting to in-sample noise.References
- Auer, P., Cesa-Bianchi, N., Fischer, P. (2002). “Finite-time Analysis of the Multiarmed Bandit Problem.” Machine Learning, 47(2-3), 235-256.
- Deb, K., Pratap, A., Agarwal, S., Meyarivan, T. (2002). “A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II.” IEEE Transactions on Evolutionary Computation, 6(2), 182-197.
- Rechenberg, I. (1973). Evolutionsstrategie: Optimierung technischer Systeme nach Prinzipien der biologischen Evolution. Frommann-Holzboog.
- Siper, S., Almog, G., Shoham, Y. et al. “ProFiT: Program Search for Financial Trading.” Preprint.
- Siper, S., Almog, G., Shoham, Y. et al. “Continuous Program Search.” SSRN 6169788.