Class: SQA::GeneticProgram
- Inherits:
-
Object
- Object
- SQA::GeneticProgram
- Defined in:
- lib/sqa/gp.rb
Defined Under Namespace
Classes: Individual
Instance Attribute Summary collapse
-
#best_individual ⇒ Object
readonly
Returns the value of attribute best_individual.
-
#crossover_rate ⇒ Object
Returns the value of attribute crossover_rate.
-
#elitism_count ⇒ Object
Returns the value of attribute elitism_count.
-
#generation ⇒ Object
readonly
Returns the value of attribute generation.
-
#generations ⇒ Object
Returns the value of attribute generations.
-
#history ⇒ Object
readonly
Returns the value of attribute history.
-
#mutation_rate ⇒ Object
Returns the value of attribute mutation_rate.
-
#population ⇒ Object
readonly
Returns the value of attribute population.
-
#population_size ⇒ Object
Returns the value of attribute population_size.
-
#stock ⇒ Object
readonly
Returns the value of attribute stock.
Instance Method Summary collapse
-
#define_genes(**constraints) ⇒ Object
Define the parameter space for evolution.
-
#evolve ⇒ Object
Run the genetic algorithm evolution.
-
#fitness(&block) ⇒ Object
Define how to evaluate fitness for an individual.
-
#initialize(stock:, population_size: 50, generations: 100, mutation_rate: 0.15, crossover_rate: 0.7, elitism_count: 2) ⇒ GeneticProgram
constructor
A new instance of GeneticProgram.
Constructor Details
#initialize(stock:, population_size: 50, generations: 100, mutation_rate: 0.15, crossover_rate: 0.7, elitism_count: 2) ⇒ GeneticProgram
Returns a new instance of GeneticProgram.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/sqa/gp.rb', line 57 def initialize(stock:, population_size: 50, generations: 100, mutation_rate: 0.15, crossover_rate: 0.7, elitism_count: 2) @stock = stock @population_size = population_size @generations = generations @mutation_rate = mutation_rate @crossover_rate = crossover_rate @elitism_count = elitism_count @population = [] @best_individual = nil @generation = 0 @history = [] @gene_constraints = {} @fitness_evaluator = nil end |
Instance Attribute Details
#best_individual ⇒ Object (readonly)
Returns the value of attribute best_individual.
54 55 56 |
# File 'lib/sqa/gp.rb', line 54 def best_individual @best_individual end |
#crossover_rate ⇒ Object
Returns the value of attribute crossover_rate.
55 56 57 |
# File 'lib/sqa/gp.rb', line 55 def crossover_rate @crossover_rate end |
#elitism_count ⇒ Object
Returns the value of attribute elitism_count.
55 56 57 |
# File 'lib/sqa/gp.rb', line 55 def elitism_count @elitism_count end |
#generation ⇒ Object (readonly)
Returns the value of attribute generation.
54 55 56 |
# File 'lib/sqa/gp.rb', line 54 def generation @generation end |
#generations ⇒ Object
Returns the value of attribute generations.
55 56 57 |
# File 'lib/sqa/gp.rb', line 55 def generations @generations end |
#history ⇒ Object (readonly)
Returns the value of attribute history.
54 55 56 |
# File 'lib/sqa/gp.rb', line 54 def history @history end |
#mutation_rate ⇒ Object
Returns the value of attribute mutation_rate.
55 56 57 |
# File 'lib/sqa/gp.rb', line 55 def mutation_rate @mutation_rate end |
#population ⇒ Object (readonly)
Returns the value of attribute population.
54 55 56 |
# File 'lib/sqa/gp.rb', line 54 def population @population end |
#population_size ⇒ Object
Returns the value of attribute population_size.
55 56 57 |
# File 'lib/sqa/gp.rb', line 55 def population_size @population_size end |
#stock ⇒ Object (readonly)
Returns the value of attribute stock.
54 55 56 |
# File 'lib/sqa/gp.rb', line 54 def stock @stock end |
Instance Method Details
#define_genes(**constraints) ⇒ Object
Define the parameter space for evolution
Example:
gp.define_genes(
indicator: [:rsi, :macd, :stoch],
period: (5..30).to_a,
buy_threshold: (20..40).to_a,
sell_threshold: (60..80).to_a
)
82 83 84 85 |
# File 'lib/sqa/gp.rb', line 82 def define_genes(**constraints) @gene_constraints = constraints self end |
#evolve ⇒ Object
Run the genetic algorithm evolution
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/sqa/gp.rb', line 108 def evolve raise "Gene constraints not defined. Call define_genes first." if @gene_constraints.empty? raise "Fitness evaluator not defined. Call fitness with a block." unless @fitness_evaluator initialize_population @generations.times do |gen| @generation = gen + 1 # Evaluate fitness for each individual evaluate_population # Track best individual current_best = @population.max_by(&:fitness) if @best_individual.nil? || current_best.fitness > @best_individual.fitness @best_individual = current_best.clone end # Record history avg_fitness = @population.sum(&:fitness) / @population.size.to_f @history << { generation: @generation, best_fitness: current_best.fitness, avg_fitness: avg_fitness, best_genes: current_best.genes.dup } # Print progress puts "Generation #{@generation}: Best=#{current_best.fitness.round(2)}%, Avg=#{avg_fitness.round(2)}%" # Create next generation @population = create_next_generation end # Final evaluation evaluate_population current_best = @population.max_by(&:fitness) if @best_individual.nil? || current_best.fitness > @best_individual.fitness @best_individual = current_best.clone end puts "\nEvolution complete!" puts "Best individual: #{@best_individual}" @best_individual end |
#fitness(&block) ⇒ Object
Define how to evaluate fitness for an individual
The block receives an individual’s genes hash and should return a numeric fitness value (higher is better)
Example:
gp.fitness do |genes|
backtest = SQA::Backtest.new(
stock: stock,
strategy: create_strategy_from_genes(genes),
initial_capital: 10_000
)
results = backtest.run
results.total_return # Higher return = higher fitness
end
102 103 104 105 |
# File 'lib/sqa/gp.rb', line 102 def fitness(&block) @fitness_evaluator = block self end |