Дисклеймер: Информация в этой статье предоставлена исключительно в образовательных и ознакомительных целях и не является финансовым, инвестиционным или торговым советом. Торговля криптовалютами сопряжена с высоким риском убытков.
У вас есть стратегия с 12 параметрами. Каждый параметр принимает ~9 значений. Вы хотите найти комбинацию, максимизирующую PnL при ограниченной просадке. Как вы это делаете?
Если ваш ответ — «перебираю все комбинации» — у вас проблема. Если ваш ответ — «меняю по одному параметру» — у вас другая проблема. Эта статья о том, какие проблемы скрываются за каждым подходом и как их решить.
Почему полный перебор невозможен
Проклятие размерности
Полный перебор (grid search) тестирует каждую комбинацию значений каждого параметра. Для двух параметров с 9 значениями это 92=81 прогон — вполне реально. Для трёх: 93=729 — терпимо.
Но для реальной стратегии с 12 параметрами:
Ngrid=912=282,429,536,481
Двести восемьдесят два миллиарда прогонов. Даже если один бэктест занимает 1 секунду (что уже оптимистично), полный перебор займёт:
T=3600×24×365282×109≈8,950лет
Это экспоненциальный рост: каждый новый параметр умножает пространство поиска в 9 раз. Добавили 13-й параметр — и вместо 9 000 лет нужно 80 000.
В статье про Parquet-кэш мы показали, как предвычисление таймфреймов и индикаторов ускоряет один бэктест до ~1 секунды. Но даже при скорости 0.1 секунды на прогон полный перебор 12 параметров потребует 895 лет. Предвычисление помогает, но не решает фундаментальную проблему экспоненциального роста.
Нужны методы, которые исследуют пространство параметров умнее, чем полный перебор.
Координатный спуск и OAT: быстрые, но слепые
Два варианта одной идеи
Есть два родственных подхода — оба оптимизируют по одному параметру за раз, но отличаются количеством проходов:
OAT (One-at-a-Time) sweep — один проход по всем параметрам. Перебрали значения первого параметра, зафиксировали лучшее, перешли ко второму — и так далее. Один раз. Быстро и дёшево.
Координатный спуск (Coordinate Descent) — многопроходный. После оптимизации последнего параметра возвращаемся к первому и проверяем, не изменился ли оптимум (ведь контекст поменялся — значения других параметров стали другими). Повторяем раунды до сходимости. Дороже, но точнее — каждый раунд может уточнять решение.
На практике для бэктестов чаще используют OAT: один проход по 12 параметрам — 96 прогонов. Координатный спуск с 3–5 раундами — 300–500 прогонов, что уже сопоставимо с Optuna, но без его преимуществ.
Для 12 параметров с ~8 значениями каждого:
NOAT=K×N=12×8=96прогонов
Сравните с 282×109 для grid search. OAT линеен: O(K⋅N) вместо O(NK). Это и его главное преимущество, и его главная проблема.
defoat_sweep(
param_grid: dict[str, list],
run_backtest_fn,
initial_params: dict,
metric: str = "effective_score",
) -> dict:
"""
OAT sweep: один проход, оптимизация по одному параметру за раз.
param_grid: {"htf_entry_sell": [0.0, 0.005, ..., 0.05], ...}
initial_params: стартовые значения всех параметров
metric: метрика для оптимизации (рекомендуется effective_score —
PnL per active time с экстраполяцией на год)
"""
best_params = initial_params.copy()
best_score = run_backtest_fn(**best_params)[metric]
for param_name, values in param_grid.items():
param_best_val = best_params[param_name]
param_best_score = best_score
for val in values:
candidate = best_params.copy()
candidate[param_name] = val
result = run_backtest_fn(**candidate)
score = result[metric]
if score > param_best_score:
param_best_score = score
param_best_val = val
best_params[param_name] = param_best_val
best_score = param_best_score
print(f"{param_name}: best={param_best_val}, score={param_best_score:.4f}")
return best_params
Какую метрику выбрать для оптимизации? Вместо raw PnL или PnL@MaxLev рекомендуется использовать effective score — PnL per active time с экстраполяцией на год. Эта метрика учитывает время в позиции и позволяет корректно сравнивать стратегии с разной частотой торговли.
Слепое пятно: взаимодействия параметров
OAT предполагает, что влияние каждого параметра аддитивно — то есть оптимальное значение одного параметра не зависит от значений других. Это допущение справедливо для некоторых параметров, но нарушается для связанных.
Аддитивные vs связанные параметры
Прежде чем оптимизировать — полезно классифицировать параметры:
Аддитивные (независимые) — оптимальное значение одного не зависит от другого. Их можно оптимизировать по одному дёшево:
htf_entry_sell и htf_entry_buy — пороги входа для разных направлений (sell/buy) на одном таймфрейме. Sell-порог фильтрует шорт-сигналы, buy-порог — лонг. Они работают на разных подмножествах сделок.
tp_target и be_trigger — take-profit и breakeven, если они не создают конфликтующих условий выхода.
Связанные (интерактивные) — оптимальное значение одного зависит от другого. Нужна совместная оптимизация:
htf_entry_sell и mtf_entry_sell — пороги для одного направления (sell) на разных таймфреймах. HTF определяет, какие сигналы дойдут до MTF, а порог MTF определяет эффективность фильтрации. Оптимум HTF сдвигается при изменении MTF.
ltf_entry_sell, mtf_entry_sell, htf_entry_sell — вся цепочка порогов одного направления.
partial_frac и tp_target — размер частичного закрытия зависит от уровня TP.
Практический подход: сначала дёшево оптимизируйте аддитивные параметры через OAT. Затем связанные группы — через Optuna. Это сокращает бюджет: вместо 12 параметров в Optuna отправляем только 6–8 связанных, а остальные уже зафиксированы.
Пример: как OAT пропускает взаимодействие
Рассмотрим два связанных порога:
htf_entry_sell — порог на старшем таймфрейме (sell direction)
mtf_entry_sell — порог на среднем таймфрейме (sell direction)
OAT фиксирует mtf_entry_sell = 0.01 (начальное значение) и перебирает htf_entry_sell. Находит лучшее значение: htf_entry_sell = 0.02. Фиксирует его и переходит к следующему параметру — больше не возвращается.
Вот что OAT пропустил:
htf_entry_sell
mtf_entry_sell
PnL
0.02
0.01
+42%
0.02
0.02
+38%
0.03
0.02
+51%
0.03
0.01
+35%
Комбинация (0.03, 0.02) даёт PnL +51%, но OAT никогда её не рассмотрит, потому что при фиксированном mtf_entry_sell = 0.01 значение htf_entry_sell = 0.03 даёт лишь +35%. OAT «застрял» в локальном оптимуме (0.02, 0.01) и не видит глобальный оптимум (0.03, 0.02).
Это классическая проблема: если ландшафт целевой функции содержит диагональные хребты (когда оптимум одного параметра сдвигается при изменении другого), OAT их пропускает.
Формализация проблемы
Пусть f(θ1,θ2,…,θK) — целевая функция (PnL). OAT находит точку, в которой:
∂θi∂f=0∀i
Но это условие необходимое, а не достаточное для глобального оптимума. Если матрица Гессиана Hij=∂θi∂θj∂2f имеет значимые недиагональные элементы — OAT не учитывает кросс-производные ∂θi∂θj∂2f при i=j.
Для связанных параметров (пороги одного направления через несколько таймфреймов) — взаимодействия скорее правило, чем исключение. Порог входа на старшем таймфрейме определяет, какие сигналы дойдут до среднего, а порог на среднем определяет эффективность фильтрации на младшем. Для аддитивных параметров (разные направления, независимые фильтры) кросс-производные близки к нулю — и OAT работает хорошо.
Bayesian optimization: умный поиск
Идея
Вместо слепого перебора или жадного поиска, Bayesian optimization строит суррогатную модель целевой функции и на каждом шаге выбирает точку, в которой ожидаемое улучшение максимально.
Алгоритм:
Выбираем несколько случайных точек, считаем целевую функцию
Строим суррогатную модель (аппроксимирует f(θ) по наблюдённым точкам)
Находим точку с максимальным ожидаемым улучшением (acquisition function)
Считаем целевую функцию в этой точке
Обновляем суррогатную модель
Повторяем шаги 3–5
Ключевое отличие от OAT: Bayesian optimization учитывает все параметры одновременно и может исследовать диагональные хребты в пространстве параметров.
TPE (Tree-structured Parzen Estimator)
TPE — сэмплер по умолчанию в Optuna. Вместо того чтобы моделировать f(θ) напрямую, TPE моделирует два распределения:
l(θ) — распределение параметров, при которых целевая функция лучше порога y∗
g(θ) — распределение параметров, при которых целевая функция хуже порога y∗
Acquisition function TPE — отношение:
EI(θ)∝g(θ)l(θ)
TPE выбирает точки, где l(θ) велико (параметры похожи на «хорошие»), а g(θ) мало (параметры не похожи на «плохие»).
Почему TPE подходит для бэктестов:
Работает с условными зависимостями между параметрами
Не требует непрерывности целевой функции
Эффективен при умеренном бюджете (100–1000 итераций)
Поддерживает категориальные и дискретные параметры
Gaussian Process (GP)
Альтернатива TPE — Gaussian Process. GP моделирует f(θ) как многомерный нормальный процесс и даёт не только прогноз значения, но и неопределённость в каждой точке.
f(θ)∼GP(m(θ),k(θ,θ′))
где m(θ) — среднее, k(θ,θ′) — ковариационная функция (kernel).
GP хорош, когда:
Параметров немного (до 10–15)
Целевая функция гладкая
Каждый прогон дорогой (минуты, часы)
Для бэктестов с предвычисленным Parquet-кэшем, где один прогон занимает ~1 секунду, TPE обычно предпочтительнее: он быстрее строит модель и лучше масштабируется на 500+ итераций.
При скорости ~1 секунда на бэктест (с предвычисленным кэшем):
T500=500×1с≈8минут
Восемь минут против 8 950 лет полного перебора. И при этом TPE в 500 итерациях находит комбинации, которые OAT пропускает за 96, потому что исследует пространство параметров одновременно, а не по одной оси.
Сохранение и возобновление исследования
import optuna
study = optuna.create_study(
storage="sqlite:///optuna_study.db",
study_name="strategy_v2",
sampler=TPESampler(seed=42),
direction="minimize",
load_if_exists=True, # продолжить, если исследование уже есть
)
study.optimize(objective, n_trials=300)
study.optimize(objective, n_trials=200)
Добавление ограничений
Не все комбинации параметров допустимы. Например, порог выхода не должен быть больше порога входа:
Сильные стороны: отлично находит взаимодействия между непрерывными параметрами, учитывает корреляции
Слабые стороны: не поддерживает категориальные параметры, требует больше итераций для инициализации
Когда использовать: если все параметры непрерывные и вы подозреваете сильные взаимодействия
GPSampler
sampler = optuna.samplers.GPSampler(seed=42)
Принцип: Gaussian Process с acquisition function
Сильные стороны: лучшая sample efficiency (меньше итераций для хорошего результата), даёт оценку неопределённости
Слабые стороны:O(n3) по числу итераций — медленный при n>200
Когда использовать: если один бэктест дорогой (минуты) и бюджет ограничен 100–200 итерациями
RandomSampler (базовая линия)
sampler = optuna.samplers.RandomSampler(seed=42)
Принцип: равномерная случайная выборка
Сильные стороны: не застревает в локальных оптимумах, полное покрытие пространства
Слабые стороны: не учитывает предыдущие результаты
Когда использовать: как baseline для сравнения, или для разведочного анализа
QMCSampler
sampler = optuna.samplers.QMCSampler(seed=42)
Принцип: Quasi-Monte Carlo (последовательности Sobol/Halton) — заполняет пространство равномернее, чем случайный сэмплер
Сильные стороны: лучшее покрытие пространства, чем RandomSampler, воспроизводимость
Слабые стороны: не адаптируется к результатам
Когда использовать: для первых 50–100 итераций перед переключением на TPE
Сводная таблица
Сэмплер
Тип
Взаимодействия
Категориальные
Лучший бюджет
TPE
Bayesian
Частично
Да
100–1000
CmaEs
Эволюционный
Да
Нет
200–2000
GP
Bayesian
Да
Ограничено
50–200
Random
Случайный
Нет
Да
Любой (baseline)
QMC
Квази-случайный
Нет
Нет
50–500
Практический бенчмарк
import optuna
import time
defbenchmark_sampler(sampler, n_trials=300):
"""Сравнение сэмплеров на одной задаче."""
study = optuna.create_study(sampler=sampler, direction="minimize")
start = time.time()
study.optimize(objective, n_trials=n_trials, show_progress_bar=False)
elapsed = time.time() - start
return {
"best_value": -study.best_value,
"elapsed_sec": elapsed,
"best_trial": study.best_trial.number,
}
samplers = {
"TPE": optuna.samplers.TPESampler(seed=42),
"CmaEs": optuna.samplers.CmaEsSampler(seed=42),
"GP": optuna.samplers.GPSampler(seed=42),
"Random": optuna.samplers.RandomSampler(seed=42),
"QMC": optuna.samplers.QMCSampler(seed=42),
}
for name, sampler in samplers.items():
result = benchmark_sampler(sampler, n_trials=300)
print(f"{name:8s}: best PnL={result['best_value']:.2f}%, "f"found at trial #{result['best_trial']}, "f"time={result['elapsed_sec']:.1f}s")
Типичные результаты для стратегии с 12 параметрами:
Сэмплер
Best PnL
Найден на итерации
Overhead сэмплера
TPE
~51%
~180
Низкий
CmaEs
~49%
~250
Средний
GP
~48%
~90
Высокий при n>200
Random
~42%
~270
Минимальный
QMC
~43%
~200
Минимальный
TPE и CmaEs стабильно обходят случайный поиск на 15–20% по итоговому PnL. GP находит хорошие результаты раньше, но упирается в вычислительный потолок при большом числе итераций.
Многокритериальная оптимизация: PnL vs MaxDD
Почему одного критерия недостаточно
Максимизация PnL без ограничений на просадку — путь к катастрофе. Стратегия с PnL +80% и MaxDD -30% при асимметрии убытков значительно рискованнее, чем стратегия с PnL +50% и MaxDD -5%.
Задача оптимизации на самом деле многокритериальная:
maxθPnL(θ)приMaxDD(θ)→min
Эти цели конфликтуют: агрессивные параметры увеличивают и PnL, и просадку. Решение — не одна точка, а фронт Парето: множество решений, в которых нельзя улучшить одну метрику, не ухудшив другую.
Фронт Парето даёт множество решений. Как выбрать одно?
defselect_from_pareto(
pareto_trials: list,
max_dd_limit: float = -5.0,
min_pnl: float = 20.0,
) -> list:
"""
Фильтрация фронта Парето по ограничениям.
max_dd_limit: максимально допустимая просадка (например, -5%)
min_pnl: минимально приемлемый PnL (%)
"""
filtered = []
for trial in pareto_trials:
pnl, max_dd = trial.values
if max_dd >= max_dd_limit and pnl >= min_pnl:
max_lev = min(50 / abs(max_dd), 100) if max_dd != 0else100
pnl_at_max_lev = pnl * max_lev
filtered.append({
"trial": trial,
"pnl": pnl,
"max_dd": max_dd,
"max_lev": max_lev,
"pnl_at_max_lev": pnl_at_max_lev,
})
filtered.sort(key=lambda x: x["pnl_at_max_lev"], reverse=True)
return filtered
Обратите внимание: при расчёте PnL при максимальном плече необходимо учитывать funding rates, иначе теоретически высокий leverage обернётся убытком на реальном рынке. Кроме того, итоговый PnL — это single-point estimate, и для оценки устойчивости результата нужен Monte Carlo bootstrap.
Пример: три стратегии на фронте Парето
Стратегия
PnL
MaxDD
MaxLev
PnL@MaxLev
Trading time
Strategy A
~55%
~0.9%
~55x
~3025%
~15%
Strategy B
~25%
~0.75%
~66x
~1650%
~5%
Strategy C
~300%
~17%
~3x
~900%
~45%
Strategy C с впечатляющим PnL +300% оказывается наименее привлекательной по PnL@MaxLev из-за высокой просадки. Strategy A лидирует по чистой доходности при плече, но с учётом PnL по активному времени Strategy B может оказаться предпочтительнее — 95% свободного времени можно заполнить другими стратегиями.
Contour plots и важность параметров
Визуализация ландшафта
После оптимизации — визуализация. Optuna предоставляет встроенные инструменты:
Contour plot строит двумерное сечение целевой функции для пары параметров. Если изолинии параллельны одной из осей — параметры не взаимодействуют, и OAT нашёл бы тот же оптимум. Если изолинии диагональные — взаимодействие есть, и OAT промахнётся.
key_params = ["htf_entry_sell", "mtf_entry_sell", "ltf_entry_sell",
"htf_entry_buy", "mtf_entry_buy", "ltf_entry_buy"]
for i, p1 inenumerate(key_params):
for p2 in key_params[i+1:]:
fig = vis.plot_contour(study, params=[p1, p2])
fig.write_image(f"contour_{p1}_vs_{p2}.png")
Если contour plot показывает плато — область, где целевая функция мало меняется — это хороший знак. Плато означает, что результат устойчив к небольшим отклонениям параметров. Подробнее об анализе плато и его связи с оверфиттингом — в готовящейся статье Plateau analysis.
Parameter importance
importance = optuna.importance.get_param_importances(study)
for param, imp in importance.items():
print(f"{param:20s}: {imp:.4f}")
Параметры с importance < 0.01 можно зафиксировать на значении по умолчанию — это снизит размерность задачи и ускорит оптимизацию. Но будьте осторожны: низкая importance может означать и то, что параметр важен только во взаимодействии с другими. Проверяйте через contour plots.
Предвычисленный кэш: почему 1 секунда на бэктест меняет всё
Скорость одного бэктеста определяет, какой метод оптимизации вы можете себе позволить.
Время бэктеста
96 OAT
500 TPE
2000 CmaEs
60 секунд
1.6 часа
8.3 часа
33 часа
10 секунд
16 минут
83 минуты
5.5 часов
1 секунда
1.5 минуты
8 минут
33 минуты
0.1 секунды
10 секунд
50 секунд
3.3 минуты
При 60 секундах на бэктест 500 итераций TPE — это 8 часов. Уже терпимо, но итерировать (изменить целевую функцию, перезапустить) дорого. При 1 секунде — 8 минут, и можно запускать десятки экспериментов за день.
Именно поэтому предвычисление в Parquet-кэш — не просто оптимизация скорости, а расширение пространства доступных методов. Без кэша вы ограничены OAT или 100 итерациями GP. С кэшем — можете позволить себе 2000 итераций CmaEs или полноценный multi-objective NSGA-III.
import pyarrow.parquet as pq
import time
t0 = time.time()
htf_pre = pq.read_table("cache/htf_indicators.parquet").to_pandas()
mtf_pre = pq.read_table("cache/mtf_indicators.parquet").to_pandas()
ltf_pre = pq.read_table("cache/ltf_indicators.parquet").to_pandas()
print(f"Cache loaded in {time.time() - t0:.2f}s") # ~0.3s
t1 = time.time()
result = run_backtest(htf_pre, mtf_pre, ltf_pre, htf_entry_sell=0.02, ...)
print(f"Backtest in {time.time() - t1:.2f}s") # ~1.0s
Практические рекомендации
Когда использовать OAT
OAT оправдан в следующих случаях:
Разведочный анализ. Вы только начинаете исследовать стратегию и хотите понять, какие параметры вообще влияют на результат. 96 прогонов за 1.5 минуты — отличная отправная точка.
Аддитивные параметры. Для параметров, которые работают на непересекающихся подмножествах сделок (sell vs buy направления, разные инструменты), OAT даст корректный результат быстрее.
Очень дорогой бэктест. Если один прогон занимает 10+ минут и ускорить его невозможно, OAT с 96 прогонами (16 часов) предпочтительнее 500 итераций TPE (3.5 дня).
Когда использовать Optuna
Optuna предпочтительнее в большинстве случаев:
Больше 3 параметров. Взаимодействия практически гарантированы — OAT пропустит оптимум.
Мультитаймфрейм-стратегии. Пороги на разных таймфреймах почти всегда взаимосвязаны.
Финальная оптимизация. Когда стратегия прошла Monte Carlo bootstrap и вы уверены в её робастности — Optuna найдёт лучшие параметры.
Многокритериальная задача. PnL vs MaxDD vs trading time — OAT не может решить эту задачу в принципе.
Гибридный подход: OAT для аддитивных + Optuna для связанных
Не обязательно выбирать между OAT и Optuna — лучше комбинировать:
Классифицируйте параметры. Разделите на аддитивные (независимые) и связанные (интерактивные). Пример для 12 separation-параметров:
Аддитивные:htf_entry_sell ↔ htf_entry_buy, mtf_entry_sell ↔ mtf_entry_buy, ltf_entry_sell ↔ ltf_entry_buy (sell/buy — разные направления, работают на непересекающихся сделках)
Связанные группа sell:htf_entry_sell, mtf_entry_sell, ltf_entry_sell (цепочка фильтрации: HTF → MTF → LTF для sell-сигналов)
Связанные группа buy:htf_entry_buy, mtf_entry_buy, ltf_entry_buy
OAT для аддитивных. Оптимизируйте sell- и buy-группы независимо. Если sell-параметры не влияют на buy-сделки — OAT даст корректный результат за минуты.
Optuna для связанных. Внутри каждой группы (sell: 6 параметров entry+exit) используйте TPE. 6 параметров вместо 12 — бюджет сокращается вдвое.
1. Предвычислить Parquet-кэш (один раз)
2. Классифицировать параметры: аддитивные vs связанные
3. OAT для аддитивных (~50 прогонов, ~1 мин) → зафиксировать
4. Optuna TPE для связанных групп (300 итераций × 2 группы, ~10 мин)
5. Optuna NSGA-III для мета-параметров (500 итераций, ~8 мин) → фронт Парето
6. Contour plots → визуализировать взаимодействия
7. Monte Carlo bootstrap лучших точек → confidence intervals
8. Walk-Forward → проверка на out-of-sample
Шаг 8 — walk-forward оптимизация — критически важен для защиты от оверфиттинга. Подробнее об этом — в готовящейся статье Walk-Forward.
Ловушки при оптимизации
Оверфиттинг. Чем больше параметров и чем точнее оптимизация — тем выше риск подогнать стратегию под исторические данные. 500 итераций Optuna с 12 параметрами найдут комбинацию, идеально работающую на обучающей выборке, но бесполезную на новых данных.
Multiple comparisons problem. Если вы тестируете 500 комбинаций, вероятность случайно найти «хороший» результат растёт. Поправка Бонферрони или контроль FDR (False Discovery Rate) помогают, но проще — валидация на out-of-sample.
Недостаточный бюджет. TPE с 50 итерациями при 12 параметрах — это мало. Первые 20 итераций — случайные (startup), остаётся всего 30 для моделирования. Минимальный бюджет: 10×K=120 итераций для 12 параметров, рекомендуемый: 30–50×K.
Freqtrade: как это устроено в production-фреймворке
Freqtrade — один из популярных фреймворков для алготрейдинга — использует Optuna под капотом через модуль Hyperopt. Его опыт подтверждает наши рекомендации:
Сэмплеры: TPE (по умолчанию), GP, CmaEs, NSGA-II, QMC — все доступны через конфигурацию
Loss functions: 12 встроенных функций потерь, включая ShortTradeDurHyperOptLoss, SharpeHyperOptLoss, MaxDrawDownHyperOptLoss
Multi-objective: поддержка NSGA-II и NSGA-III для одновременной оптимизации нескольких метрик
Custom samplers: возможность подключить любой Optuna-совместимый сэмплер
Ключевой урок из экосистемы Freqtrade: встроенные loss functions покрывают типовые сценарии, но для серьёзной оптимизации нужна кастомная целевая функция, учитывающая специфику вашей стратегии — активное время, funding costs, adaptive drill-down для точного fill simulation.
Заключение
Координатный спуск (OAT) — быстрый и интуитивно понятный метод. Для 12 параметров он требует всего 96 прогонов и работает за полторы минуты. Но он слеп к взаимодействиям параметров — а в мультитаймфрейм-стратегиях взаимодействия есть почти всегда.
Bayesian optimization через Optuna (TPE, GP, CmaEs) исследует пространство параметров целиком. 500 итераций за 8 минут — с предвычисленным Parquet-кэшем — находят комбинации, невидимые для OAT.
Многокритериальная оптимизация (NSGA-III) превращает задачу «максимизировать PnL» в задачу «построить фронт Парето PnL vs MaxDD» — и даёт набор решений с разным балансом доходности и риска.
Но оптимизация — это лишь часть пайплайна. Найденные параметры нужно проверить через Monte Carlo bootstrap, скорректировать на funding rates, пересчитать с учётом активного времени, и провести через walk-forward validation. Об этом — в следующих статьях серии.
@article{soloviov2026optuna,
author = {Soloviov, Eugen},
title = {Координатный спуск vs Bayesian optimization: что находит лучшие параметры},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/optuna-vs-coordinate-descent},
description = {Почему полный перебор невозможен для 12+ параметров, как координатный спуск пропускает взаимодействия, и как Optuna с TPE-сэмплером за 500 итераций находит то, что OAT не может за 96.}
}