Skip to main content
Ultra Feature. Requires an Ultra subscription. Get started at api.mathematicalcompany.com

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 calls hz.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. Pick tournament_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.

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 probability rate 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 where fronts[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 for patience 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.

hz.evolve_nsga2

Multi-objective evolution using NSGA-II. Returns the Pareto front.

hz.evolve_islands

Island model evolution with parallel populations and periodic migration.

Examples

Optimize Market Making Parameters

Multi-Objective: Sharpe vs Drawdown

Island Model with Diverse Selection


Mathematical Background

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).
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.
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.
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.
Non-dominated Sorting Genetic Algorithm II for multi-objective optimization:
  1. 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.
  2. 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.
  3. Selection: Prefer lower-rank fronts. Within the same front, prefer higher crowding distance (less crowded regions).
Individual A dominates B iff A is at least as good as B on all objectives and strictly better on at least one.
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
Per-island selection specialization (Roulette, Rank, UCB1, Epsilon-Greedy) ensures diverse search strategies. Migration spreads good genetic material across populations while maintaining local diversity.
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.
Evolution stops early when:
  1. Patience exhausted: No improvement exceeding min_improvement for patience consecutive generations.
  2. Diversity collapse: Population diversity (mean pairwise L2 distance) falls below diversity_threshold, indicating premature convergence.
Both checks run each generation. Early stopping prevents wasting compute on stagnant populations.
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.