Разработка системы оптимизации параметров стратегии (genetic algorithm)

Отметим: когда мы беремся за оптимизацию торговой стратегии, первая боль — ручной перебор десятков комбинаций или Grid Search, который при 7 параметрах требует ~100 тыс. бэктестов. На одном из проектов клиент потратил две недели на перебор — и получил локальный оптимум. Мы внедрили генетический алго

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1450
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1309
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1004
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1270
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    719
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1011

Отметим: когда мы беремся за оптимизацию торговой стратегии, первая боль — ручной перебор десятков комбинаций или Grid Search, который при 7 параметрах требует ~100 тыс. бэктестов. На одном из проектов клиент потратил две недели на перебор — и получил локальный оптимум. Мы внедрили генетический алгоритм (GA) и сократили время поиска до 1 дня. GA решает проблему комбинаторного взрыва: вместо полного перебора он выращивает популяцию решений через отбор, скрещивание и мутацию. Эффективность особенно заметна для пространств с 5+ параметрами, где Grid Search становится непрактичным. Наша реализация на Python с библиотекой DEAP даёт прирост скорости до 50× без потери качества.

Как генетический алгоритм решает проблему комбинаторного взрыва?

В основе GA лежит эволюционная модель. Каждая особь — набор параметров (периоды скользящих, коэффициенты стоп-лосса, пороги RSI). Популяция эволюционирует через отбор лучших (по Sharpe ratio), скрещивание (blend crossover) и мутацию (гауссов шум). Мы используем DEAP — зрелый фреймворк с поддержкой параллельных вычислений. Это позволяет обрабатывать до 60 особей за поколение за секунды. Для 10 параметров с 10 градациями полный перебор дал бы 10 млрд комбинаций, а GA находит хорошее решение за 2000–5000 итераций.

Какие проблемы решаем?

  • Комбинаторный взрыв: 10 параметров с 10 градациями = 10 млрд комбинаций. GA находит хорошее решение за 2000–5000 итераций.
  • Переобучение: эволюция легко запоминает шум. Мы встраиваем штрафы за малое число сделок (<20) и проверяем на out-of-sample данных.
  • Несовместимость с блэкбоксами: наши оптимизаторы работают с любым бэктест-движком через callback-функцию.

Как избежать переобучения при эволюционной оптимизации?

Переобучение — одна из главных ловушек. Мы применяем кросс-валидацию по временным периодам (walk-forward), штрафуем за сложность модели и обязательно проверяем лучшие решения на независимом out-of-sample наборе данных. Например, если стратегия показывает Sharpe 2.5 на тренировочных данных, но 0.3 на валидации — такой набор отбрасывается. Финальный результат всегда подтверждается на свежих рыночных данных.

Сравнение методов оптимизации

Метод Количество итераций (7 параметров) Риск переобучения Время выполнения
Grid Search 10 млн Высокий Недели
Random Search 10 тыс. Средний Дни
Genetic Algorithm 2–5 тыс. Низкий (с валидацией) Часы

Как мы это делаем?

На одном проекте для крипто-арбитражной стратегии мы оптимизировали 7 параметров (периоды скользящих, RSI, стоп-лосс, тейк-профит). Использовали DEAP с population_size=60, поколений=40. Фитнес-функция — Sharpe ratio, с штрафом за <20 сделок. Результат: Sharpe 2.1 против 0.8 у ручного подбора. Согласно документации DEAP, параллельная оценка на 4 ядрах ускоряет работу в 2–3 раза.

from deap import base, creator, tools, algorithms import random import numpy as np from functools import partial # Определяем задачу максимизации Sharpe ratio creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax) class GeneticOptimizer: def __init__( self, param_bounds: dict[str, tuple], # {'param': (min, max)} backtest_fn: callable, population_size: int = 50, n_generations: int = 30, crossover_prob: float = 0.7, mutation_prob: float = 0.2, n_jobs: int = 4, ): self.param_names = list(param_bounds.keys()) self.param_bounds = list(param_bounds.values()) self.backtest_fn = backtest_fn self.pop_size = population_size self.n_gen = n_generations self.cx_prob = crossover_prob self.mut_prob = mutation_prob self.n_jobs = n_jobs def decode_individual(self, individual: list) -> dict: """Конвертируем список значений [0,1] в реальные параметры""" params = {} for i, name in enumerate(self.param_names): low, high = self.param_bounds[i] if isinstance(low, int) and isinstance(high, int): # Целочисленный параметр params[name] = int(round(low + individual[i] * (high - low))) else: # Вещественный параметр params[name] = low + individual[i] * (high - low) return params def evaluate(self, individual: list) -> tuple: """Функция fitness: запускаем бэктест, возвращаем Sharpe ratio""" params = self.decode_individual(individual) try: metrics = self.backtest_fn(params) sharpe = metrics.get('sharpe_ratio', 0) # Штраф за слишком мало сделок trades = metrics.get('total_trades', 0) if trades < 20: sharpe *= trades / 20 return (sharpe,) except Exception: return (-999.0,) def run(self) -> tuple[dict, pd.DataFrame]: toolbox = base.Toolbox() # Генератор особей: каждый параметр = float в [0, 1] toolbox.register("attr_float", random.random) toolbox.register( "individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=len(self.param_names), ) toolbox.register("population", tools.initRepeat, list, toolbox.individual) toolbox.register("evaluate", self.evaluate) toolbox.register("mate", tools.cxBlend, alpha=0.3) # Blend crossover toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=0.1, indpb=0.2) toolbox.register("select", tools.selTournament, tournsize=3) # Ограничиваем значения в [0, 1] после мутации def check_bounds(individual): for i in range(len(individual)): individual[i] = max(0.0, min(1.0, individual[i])) return individual, toolbox.decorate("mutate", check_bounds) toolbox.decorate("mate", check_bounds) # Параллельная оценка if self.n_jobs > 1: from multiprocessing.pool import Pool pool = Pool(self.n_jobs) toolbox.register("map", pool.map) # Запуск эволюции population = toolbox.population(n=self.pop_size) stats = tools.Statistics(lambda ind: ind.fitness.values[0]) stats.register("max", np.max) stats.register("avg", np.mean) hof = tools.HallOfFame(10) # Топ-10 лучших особей population, logbook = algorithms.eaSimple( population, toolbox, cxpb=self.cx_prob, mutpb=self.mut_prob, ngen=self.n_gen, stats=stats, halloffame=hof, verbose=True, ) if self.n_jobs > 1: pool.close() # Результаты best_params = self.decode_individual(hof[0]) all_results = [] for ind in hof: params = self.decode_individual(ind) all_results.append({**params, 'sharpe': ind.fitness.values[0]}) return best_params, pd.DataFrame(all_results) 
Типичные ошибки при оптимизации GA
  • Слишком маленькая популяция (<30) приводит к преждевременной сходимости.
  • Слишком высокая вероятность мутации (>0.5) разрушает хорошие решения.
  • Отсутствие out-of-sample валидации — гарантия переобучения.
  • Игнорирование ограничений (min/max параметров) может дать нереализуемые комбинации.

Что входит в работу?

  • Адаптируемый код оптимизатора под ваш стек
  • Документация по настройке и запуску
  • Поддержка при интеграции в вашу систему
  • Рекомендации по улучшению стратегии на основе результатов

Ориентировочные сроки

Этап Время
Аналитика и настройка фитнес-функции 1–3 дня
Разработка оптимизатора под ваш стек 3–5 дней
Тестирование и проверка на out-of-sample 2–4 дня
Документирование и передача 1–2 дня

Сроки зависят от сложности стратегии и количества параметров. Стоимость рассчитывается индивидуально.

Как проходит процесс?

  1. Аналитика: разбираем вашу стратегию, определяем параметры для оптимизации и границы.
  2. Проектирование: пишем фитнес-функцию с учётом ваших метрик (Sharpe, Sortino, drawdown).
  3. Реализация: настраиваем GA на DEAP или Foundry (для смарт-контрактов).
  4. Тест: запускаем эволюцию, сравниваем с baseline, проверяем на out-of-sample.
  5. Деплой: выдаём код оптимизатора и топ-10 решений с документацией.

Внедрение GA окупается, если вы тратите недели на ручной подбор или Grid Search. Наша команда имеет многолетний опыт в оптимизации торговых алгоритмов. Свяжитесь с нами — мы оценим ваш проект и предложим решение. Получите консультацию, чтобы обсудить детали.

Почему выбирают нас?

  • Более 30 проектов по оптимизации стратегий
  • Используем только open-source инструменты (DEAP, Pandas) — никаких вендор-локов
  • Полная прозрачность: вы получаете исходный код и документацию