Strategy Evolution Engine¶
StratEvo includes a self-improving evolution engine that automatically optimizes trading strategies through iterative mutation and evaluation. Inspired by evolutionary algorithms, it evolves your YAML strategy definitions to find better parameters and rule combinations.
How It Works¶
The evolution loop follows these steps:
- Seed — Evaluate the initial strategy and add it to the frontier
- Select — Pick the best strategy from the frontier as a parent
- Analyze — Identify weaknesses (losing trades, poor risk metrics)
- Propose — Generate mutation proposals (parameter tweaks, rule changes)
- Mutate — Apply the best proposal to create a child strategy
- Evaluate — Backtest the mutated child strategy
- Update — Accept the child if it outperforms the worst in the frontier
- Repeat — Continue until
max_generationsor early-stop
Quick Start¶
CLI¶
# Evolve a strategy YAML with default settings
stratevo evolve my_strategy.yaml --symbol AAPL --generations 20
# With custom parameters
stratevo evolve my_strategy.yaml \
--symbol NVDA \
--generations 50 \
--frontier-size 5 \
--start 2022-01-01
Python API¶
from stratevo.evolution.engine import EvolutionEngine, EvolutionConfig
from stratevo.strategy.expression import OHLCVData
# Load your OHLCV data
data = OHLCVData(dates=dates, opens=opens, highs=highs,
lows=lows, closes=closes, volumes=volumes)
# Read your seed strategy
with open("strategies/builtin/golden-cross-momentum.yaml") as f:
seed = f.read()
# Configure and run
config = EvolutionConfig(
max_generations=20, # Max iterations
frontier_size=3, # Keep top-N strategies
no_improvement_limit=5, # Stop after N stale generations
)
engine = EvolutionEngine(config=config)
result = engine.run(seed, data)
print(f"Best score: {result['best_score'].composite():.4f}")
print(f"Generations: {result['generations_run']}")
print(f"Best strategy:\n{result['best_strategy']}")
Components¶
EvolutionConfig¶
| Parameter | Type | Default | Description |
|---|---|---|---|
max_generations |
int | 10 | Maximum evolution iterations |
frontier_size |
int | 3 | Number of top strategies to retain |
no_improvement_limit |
int | 5 | Early stop after N generations without improvement |
Evaluator¶
Backtests strategies and returns a FitnessScore with metrics like return, Sharpe ratio, max drawdown, and win rate.
from stratevo.evolution.evaluator import Evaluator, FitnessScore
evaluator = Evaluator()
score: FitnessScore = evaluator.evaluate(strategy_yaml, data)
print(score.composite()) # Weighted fitness score
Proposer¶
Analyzes strategy failures and proposes targeted mutations:
from stratevo.evolution.proposer import Proposer
proposer = Proposer()
analyses = proposer.analyze(strategy_yaml, feedback)
proposals = proposer.propose(strategy_yaml, analyses)
Mutator¶
Applies proposed changes to strategy YAML:
from stratevo.evolution.mutator import Mutator
mutator = Mutator()
new_yaml = mutator.mutate(parent_yaml, proposal, feedback=feedback)
Frontier¶
Maintains a sorted set of the best strategies discovered:
from stratevo.evolution.frontier import Frontier
frontier = Frontier(max_size=5)
entry = frontier.update(strategy_yaml, score, generation=1)
best = frontier.best
parent = frontier.select_parent(strategy="best")
Callbacks¶
Track progress with the on_generation callback:
def progress(gen: int, score, strategy_yaml: str):
print(f"Gen {gen}: score={score.composite():.4f}")
result = engine.run(seed, data, on_generation=progress)
Tips¶
- Start simple — Use a well-tested seed strategy; evolution refines, it doesn't create from scratch
- Sufficient data — Provide at least 1–2 years of daily data for meaningful evaluations
- Frontier size — Larger frontiers maintain diversity but slow convergence
- Monitor overfitting — Compare evolved strategy on out-of-sample data after evolution