В этой статье мы описываем реализацию нового инструмента, который мы выпустили для набора инструментов Python Quantiacs: быстрого оптимизатора для проверки надежности торговой системы.

Как заметил Дональд Кнут, преждевременная оптимизация - это корень всех зол. Это известное высказывание среди разработчиков программного обеспечения, и оно справедливо также в контексте разработки торговых систем.

Очень заманчиво написать алгоритм, простую стратегию следования за трендом или более совершенную систему на основе машинного обучения, а затем искать оптимальные параметры или гиперпараметры, которые будут максимизировать заданная функция оценки, обычно это коэффициент Шарпа.

Поскольку оптимальные параметры максимизируют выбранную функцию оценки в прошлом, а финансовые данные очень зашумлены, обычным результатом оптимизации параметров является переоснащение: торговая система настолько хорошо работает с прошлыми данными, что становится бесполезной для прогнозирование будущих движений рынка.

Проблема завышения смоделированной производительности торговой системы выходит за рамки оптимизаторов тестирования на исторических данных: например, разработчики, как правило, сосредотачиваются и сообщают только о положительных результатах из всех моделей, которые они пробуют, - проблема, известная как систематическая ошибка выбора. Подробное описание этих проблем мы отсылаем к статье 2014 года Маркоса Лопеса де Прадо.

Тем не менее, оптимизаторы полагаются на базовую функциональность, которую можно использовать для проверки устойчивости торговой системы: сканирование системы по сетке по возможным комбинациям параметров. Результаты сканирования можно использовать для визуализации и проверки того, насколько производительность торговой системы чувствительна к выбору параметров. Надежная система будет иметь хороший коэффициент Шарпа для широкого диапазона независимых параметров.

Оптимизация торговой системы с помощью Quantiacs

Вы можете найти наш оптимизатор в разделе «Примеры» области «Разработка» вашей учетной записи:

Для запуска примера просто нажмите кнопку Клонировать и работайте в своей любимой среде, Jupyter Notebook или JupyterLab. В качестве альтернативы вы можете загрузить локально набор инструментов Quantiacs и воспользоваться преимуществами распараллеливания на своем компьютере.

Разберем код. Первым делом импортируем необходимые библиотеки:

import qnt.data as qndata
import qnt.ta as qnta
import qnt.output as qnout
import qnt.stats as qns
import qnt.log as qnlog
import qnt.optimizer as qnop
import qnt.backtester as qnbt

import xarray as xr

В дополнение к библиотеке Quantiacs, свободно доступной на нашей странице GitHub, мы импортируем xarray для быстрой обработки многомерных структур данных.

Затем мы определяем простое торговое правило на основе двух параметров. Стратегия открывает длинную позицию только тогда, когда скорость изменения линейно-взвешенной скользящей средней за последние wma_period торговых дней (в данном случае 20) за последние roc_period торговых дней (в данном случае 10) положительна:

def single_pass_strategy(data, wma_period=20, roc_period=10):
    wma = qnta.lwma(data.sel(field='close'), wma_period)
    sroc = qnta.roc(wma, roc_period)
    weights = xr.where(sroc > 0, 1, 0)
    weights = weights / len(data.asset)
    with qnlog.Settings(info=False, err=False):
        weights = qnout.clean(weights, data, debug=False)
    return weights

Стратегия возвращает веса распределения (долю инвестируемого капитала) на каждый день. Проверяем работоспособность данной стратегии с выбранными параметрами:

data = qndata.futures.load_data(min_date='2004-01-01')
single_pass_output = single_pass_strategy(data)
single_pass_stat = qns.calc_stat(data,\
    single_pass_output.sel(time=slice('2006-01-01', None)))
display(single_pass_stat.to_pandas().tail())

Код возвращает значения соответствующих статистических показателей, включая коэффициент Шарпа, в виде таблицы с начала периода выборки:

Затем мы запускаем код оптимизации, который выполняет сканирование по заранее определенному диапазону значений параметров: пользователь может выбрать для каждого параметра начальное значение, последнее и шаг:

data = qndata.futures.load_data(min_date='2004-01-01')

result = qnop.optimize_strategy(
    data,
    single_pass_strategy,
    qnop.full_range_args_generator(
        wma_period=range(10, 150, 5), # min, max, step
        roc_period=range(5, 100, 5)   # min, max, step
    ),
    workers=1 # you can set more workers on your PC
)

qnop.build_plot(result)

print("---")
print("Best iteration:")
display(result['best_iteration'])

Код возвращает интерактивный график, на котором можно проанализировать зависимость ключевых статистических показателей от параметров. На этом графике мы отображаем два независимых параметра по оси x и y, значение коэффициента Шарпа по оси z и используем разные цвета в соответствии с максимальной просадкой.

Для справки мы отображаем оптимальные значения параметров, которые максимизируют коэффициент Шарпа (остерегайтесь переобучения!):

Надежная торговая система будет иметь плавную зависимость коэффициента Шарпа от значений параметров, в идеале с хорошими значениями больше 1 для широкого выбора параметров. В переоборудованной системе обычно отображается пик коэффициента Шарпа для конкретного выбора независимых параметров.

У вас есть вопросы или предложения? Разместите их на нашем Форуме или напишите нам на [email protected]!

Вы можете попробовать оптимизатор на Quantiacs. Код можно найти в разделе Примеры области Разработка вашей учетной записи. Или вы можете загрузить полный код из нашего репозитория GitHub.