Адаптивное моделирование данных финансового рынка

Какой алгоритм дерева решений с градиентным усилением лучше подходит для адаптивного моделирования данных финансового рынка, XGBoost или CatBoost? Существует множество статей, сравнивающих эти алгоритмы на произвольных статических наборах данных, но как они работают в живой, хаотичной среде? Как насчет использования ресурсов, таких как среднее время обучения, среднее время вывода, загрузка ЦП и потребление ОЗУ? И, наконец, насколько хорошо эти прогнозы превращаются в прибыль? Чтобы ответить на эти вопросы, мы разработали эталонный эксперимент, запустили его в прямом эфире и собрали результаты. Спойлер — XGBoost был намного быстрее и выиграл почти в 4 раза с точки зрения прибыльности.

Зачем нужно адаптивное моделирование?

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

Рисунок 1. Адаптивное моделирование системы, которая изменяется с течением времени, требует обучения новых моделей, когда новые данные становятся доступными.

Адаптивное моделирование сводится к управлению данными. Данные передаются в модель как для повторного обучения, так и для вывода. В живой среде переобучение и умозаключение происходят одновременно. Между тем, конвейер обработки данных также требует пристального внимания с точки зрения разработки функций, нормализации, удаления выбросов и любых других методов манипулирования.

ФрекАИ

С помощью FreqAI [1] мы собрали воедино все эти различные механики, чтобы вы могли запускать адаптивное моделирование данных рынка криптовалют с помощью удобного, проверенного сообществом интерфейса с открытым исходным кодом, используя любую внешнюю библиотеку машинного обучения по вашему выбору.

FreqAI построен на основе программного обеспечения для алгоритмической торговли с открытым исходным кодом Freqtrade, которое обеспечивает доступ к различным API-интерфейсам обмена с открытым исходным кодом и предоставляет набор инструментов анализа и визуализации данных для оценки производительности как в реальном времени, так и для тестирования на истории. Кроме того, FreqAI в настоящее время предоставляет 18 предварительно настроенных моделей прогнозирования для XGBoost, CatBoost, LightGBM, PyTorch и Stable Baselines, а также содержит ряд пользовательских алгоритмов и методологий, направленных на улучшение вычислительной и прогностической производительности.

Чтобы попробовать программное обеспечение, все, что вам нужно сделать, это установить Freqtrade и выполнить следующую команду:

freqtrade trade --config config_examples/config_freqai.example.json --strategy FreqaiExampleStrategy --freqaimodel LightGBMRegressor --strategy-path freqtrade/templates

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

freqtrade trade --strategy QuickAdapterV3 --freqaimodel XGBoostRegressor
freqtrade trade --strategy QuickAdapterV3 --freqaimodel CatboostRegressor

Весь код, относящийся к этому эксперименту, имеет открытый исходный код и доступен для проверки/воспроизведения. Базовый исходный код FreqAI доступен на Github по адресу https://github.com/freqtrade/freqtrade. Между тем, файлы стратегии и конфигурации доступны в дискорде FreqAI.

Детали эксперимента

Чтобы продемонстрировать возможности FreqAI, мы использовали два самых популярных регрессора машинного обучения — XGBoost и CatBoost — для проведения 3-недельного исследования их производительности на живом предиктивном моделировании хаотичных данных временных рядов с рынка криптовалют.

В период с 16 февраля по 12 марта два экземпляра FreqAI, по одному для каждого регрессора, были настроены для обучения отдельных моделей для 19 пар монет (/USDT). Инстансы были размещены на отдельных, идентичных, переработанных серверах (12-ядерный Xeon X5660 с тактовой частотой 2,8 ГГц, 64 ГБ DDR3). Все серверы были протестированы, чтобы подтвердить, что они имеют одинаковую производительность.

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

Регрессоры были запущены с настройками по умолчанию, за исключением установки количества оценок для XGBoost равным 1000 (что является значением по умолчанию для CatBoost по сравнению с XGBoost по умолчанию, равным 100), чтобы продемонстрировать их поведение по умолчанию при применении к этому типу проблемы.

В целом, кластер активно генерировал 38 моделей (по 2 на монету x 19 монет) с ~ 3,3 тыс. функций для каждой каждые 2 часа.

Проектирование характеристик

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

Набор функций для каждой пары монет содержал 42 базовых индикатора, рассчитанных для базового свечного таймфрейма (5 минут), а также для 15 минут, 1 часа и 4 часов с использованием библиотек с открытым исходным кодом TA-Lib, Pandas-TAQTPyLib и Freqtrade. Подмножество индикаторов было рассчитано каждый для нескольких периодов времени (8 минут, 16 минут и 32 минуты), и каждый сдвинул 3 свечи, чтобы добавить информацию о давности. Мы также добавили день недели и час дня в качестве функций и использовали BTC и ETH в качестве коррелированных данных для всех других пар монет. В общей сложности это составило 3266 функций для каждой пары монет, за исключением BTC и ETH, которые имели по 2178 функций.

Компоненты кода для разработки функций доступны в дискорде FreqAI, а конфигурация для разработки функций довольно проста:

"freqai": {
...
"feature_parameters": {
"include_corr_pairlist": [
"BTC/USDT:USDT",
"ETH/USDT:USDT"
],
"include_timeframes": [
"5m",
"15m",
"1h",
"4h"
],
"label_period_candles": 100,
"include_shifted_candles": 3,
"DI_threshold": 20,
"weight_factor": 0.9,
"indicator_periods_candles": [8, 16, 32],
"noise_standard_deviation": 0.02,
"buffer_train_data_candles": 100
},
...
}

Цели

Тренировочные метки были определены как точки экстремумов (минимума и максимума) в скользящем окне в 200 свечей (1000 минут). Мы определили метку как &s-extrema со значением -1 для минимумов и 1 для максимумов и передали фильтр Гаусса, чтобы сгладить их, чтобы улучшить регрессию. Код для воспроизведения этих меток в FreqAI можно найти ниже:

from scipy.signal import argrelextrema
def set_freqai_targets(self, dataframe, **kwargs):
"""
Set targets for FreqAI model. Any column prepended with `&`
will be treated as a training target.
"""
dataframe["&s-extrema"] = 0
min_peaks = argrelextrema(
dataframe["low"].values, np.less,
order=100
)
max_peaks = argrelextrema(
dataframe["high"].values, np.greater,
order=100
)
for mp in min_peaks[0]:
dataframe.at[mp, "&s-extrema"] = -1
for mp in max_peaks[0]:
dataframe.at[mp, "&s-extrema"] = 1
dataframe["minima-exit"] = np.where(
dataframe["&s-extrema"] == -1, 1, 0)
dataframe["maxima-exit"] = np.where(dataframe["&s-extrema"] == 1, 1, 0)
dataframe['&s-extrema'] = dataframe['&s-extrema'].rolling(
window=5, win_type='gaussian', center=True).mean(std=0.5)
return dataframe

Это определение метки означает две вещи: 1) у нас есть несбалансированная проблема классификации и 2) мы используем регрессию для задачи классификации:

1. Идентификация точек экстремумов внутри скользящего окна из 200 свечей означает, что у нас будет только 2 истинных положительных результата (один максимальный и один минимальный), но 198 истинных отрицательных.

2. Мы хотим предсказать, соответствует ли входящая свеча максимуму или минимуму или ни тому, ни другому, что означает, что мы имеем дело с многоклассовой классификационной задачей.

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

Адаптивное пороговое значение

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

num_candles = 600 # 50 hours of 5 minute candles
pred_df_full = self.dd.historic_predictions[pair].tail(num_candles).reset_index(drop=True)
pred_df_sorted = pd.DataFrame()

# sort each column independently
for col in pred_df_sorted:
pred_df_sorted[col] = pred_df_sorted[col].sort_values(
ascending=False, ignore_index=True)

# number of expected max mins during the last 50 hours
frequency = num_candles / 200
# get the mean of the top and bottom candles, use this for the threshold
maxima_sort_threshold = pred_df_sorted.iloc[:int(frequency)].mean()
minima_sort_threshold = pred_df_sorted.iloc[-int(frequency):].mean()

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

Обнаружение выбросов

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

Индекс несходства (DI) направлен на количественную оценку неопределенности, связанной с каждым прогнозом, сделанным моделью, путем сравнения входящих данных, используемых для прогноза, с данными обучения. Если данные прогнозирования находятся далеко от обучающих данных, модель не сможет правильно их оценить, и с полученным прогнозом не следует действовать.

Как и в случае с превращением регрессорных прогнозов в двоичные решения, DI нуждается в пороговом значении для сравнения, чтобы определить, близки ли данные прогнозирования к обучающим данным или нет. Здесь мы подогнали исторические (предыдущие 50 часов) данные DI к распределению Вейбулла и использовали процентиль 0,999 в качестве порогового значения. Выбор значения отсечения будет влиять на то, насколько консервативным является DI, поскольку он позволит действовать на более или менее разнородные данные прогнозирования.

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

Рисунок 2. Индекс несходства (DI) и отсечка DI для обнаружения выбросов для BTC/USDT на протяжении всего эксперимента для каждого из двух регрессоров. Цена закрытия BTC / USDT показана в фоновом режиме, чтобы дать некоторое представление о рыночных условиях относительно производительности регрессоров. Мы видим, как изменения рыночного режима (сплошные стрелки) идентифицируются DI, пересекающим отсечку DI (пунктирные стрелки).

Торговая стратегия

Прогнозы регрессоров использовались для определения того, следует ли входить в сделку или выходить из нее:
— Если регрессор предсказал, что входящая свеча была максимумом или минимумом, и она в данный момент не находится в сделке, он войдет в длинную (для прогнозируемого максимума) или короткую (для прогнозируемого минимума).
— Если регрессор предсказал, что входящая свеча была максимумом или минимумом и она находилась в сделке, он выходил из длинной позиции, если предсказывал минимум, или выходил из короткой, если предсказывал максимум. Это было определено в FreqAI с помощью populate_entry_trend() в стратегии:

def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
"""
Define the entry criteria for going long or short.
"""
enter_long_conditions = [
df["do_predict"] == 1,
df["DI_catch"] == 1,
df["&s-extrema"] < df["minima_sort_threshold"],
]

if enter_long_conditions:
df.loc[
reduce(lambda x, y: x & y, enter_long_conditions), [
"enter_long", "enter_tag"]
] = (1, "long")

enter_short_conditions = [
df["do_predict"] == 1,
df["DI_catch"] == 1,
df["&s-extrema"] > df["maxima_sort_threshold"],
]

if enter_short_conditions:
df.loc[
reduce(lambda x, y: x & y, enter_short_conditions), [
"enter_short", "enter_tag"]
] = (1, "short")

return df

Тем не менее, у нас были и другие ограждения, которые помогли повысить производительность:
— В предыдущем тесте мы заметили, что слишком долгое пребывание в сделке обычно приводило к низкой прибыли. Из-за этого мы ограничиваем продолжительность сделок до 24 часов, и любая сделка, достигшая этого лимита, была закрыта.
— Стоп-лосс -4% был установлен для выхода из любых сделок, достигших -4% прибыли.
— Если расчет цели выявил экстремумы в самой последней свече, и это еще не было предсказано регрессором, активная длинная сделка будет закрыта, если идентифицированные экстремумы были минимальными, а активная короткая сделка будет закрыта, если идентифицированные экстремумы были максимальными.
— Как обсуждалось в разделе выше об обнаружении выбросов, пользовательский метод обнаружения выбросов FreqAI, индекс несходства, использовался для принятия решения о том, будут ли проигнорированы прогнозируемые экстремумы или нет.

Эти дополнительные компоненты были обработаны в методе custom_exit() в стратегии:

def custom_exit(
self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, **kwargs
):
"""
User defines custom trade exit criteria
"""
dataframe, _ = self.dp.get_analyzed_dataframe(
pair=pair, timeframe=self.timeframe)

last_candle = dataframe.iloc[-1].squeeze()
trade_date = timeframe_to_prev_date(
self.timeframe, (trade.open_date_utc -
timedelta(minutes=int(self.timeframe[:-1])))
)
trade_candle = dataframe.loc[(dataframe["date"] == trade_date)]

if trade_candle.empty:
return None
trade_candle = trade_candle.squeeze()

entry_tag = trade.enter_tag

trade_duration = (current_time - trade.open_date_utc).seconds / 60

if trade_duration > 1000:
return "trade expired"

if last_candle["DI_catch"] == 0:
return "Outlier detected"

if (
last_candle["&s-extrema"] < last_candle["minima_sort_threshold"]
and entry_tag == "short"
):
return "minimia_detected_short"

if (
last_candle["&s-extrema"] > last_candle["maxima_sort_threshold"]
and entry_tag == "long"
):
return "maxima_detected_long"

Результаты

Точность модели

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

Сбалансированная точность была рассчитана на основе скользящего окна в 600 свечей (50 часов).

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

Здесь мы смотрим на временную разницу между прогнозом и его ближайшей целью, нормализованную к скользящему окну в 200 свечей (1000 минут), которое мы использовали для идентификации целей. Идеальное совпадение означает, что у нас есть временная точность, равная 1, в то время как прогноз на расстояние более 1000 минут дает отрицательное значение.

На рисунке ниже показаны две метрики точности на протяжении всего эксперимента для регрессора XGBoost, предсказывающие экстремумы для BTC/USDT. Сбалансированная точность обновлялась на каждой свече, в то время как временная точность обновлялась только тогда, когда были предсказаны экстремумы.

Рисунок 3. Сбалансированные (вверху) и временные (внизу) оценки точности для экстремумов предсказания регрессора XGBoost для BTC/USDT. Скользящее окно в 50 часов, используемое для расчета сбалансированной точности, обозначено на верхнем графике заштрихованной областью. Все регрессоры и монеты отслеживались аналогичным образом через нашу панель управления в реальном времени.

Из таблицы 1 мы видим, что регрессоры показали схожие результаты как с точки зрения сбалансированной, так и временной точности.

Таблица 1. Среднее плюс/минус стандартное отклонение точности модели для отдельных регрессоров.

Поскольку наша цель была определена как точки экстремумов в пределах окна в 1000 минут, модели были обучены ожидать один минимум/максимум каждые 16,7 часа. Как видно из таблицы 2, регрессоры предсказывали в два раза большее количество экстремумов по сравнению с количеством выявленных целей.

Таблица 2. Среднее плюс/минус стандартное отклонение соотношения между прогнозами и целевыми показателями для отдельного регрессора

Использование ресурсов

Использование ресурсов (таблица 3) для двух регрессоров показывает, что CatBoost был медленным как с точки зрения обучения, так и с точки зрения прогнозирования по сравнению с XGBoost. Поскольку новые данные поступали каждые 5 минут, модели, созданные CatBoost, не всегда обучались с использованием самых последних доступных данных, поскольку регрессор слишком медленно завершал обучение модели. Однако благодаря распараллеленной архитектуре FreqAI всегда есть модель, доступная для вывода.

Таблица 3. Среднее плюс/минус стандартное отклонение использования ресурсов в течение 3-недельного эксперимента для отдельных регрессоров.

Рентабельность

Несмотря на одинаковые входные функции, регрессоры XGBoost и CatBoost показали очень разные результаты с точки зрения прибыльности (таблица 4). В конце эксперимента оба были в прибыли, но XGBoost явно превзошел своего конкурента, получив прибыль в 7% по сравнению с 2% для CatBoost.

Таблица 4. Среднее плюс/минус стандартное отклонение прибыли на закрытие (% от суммы ставки) и итоговой совокупной прибыли (% от общего кошелька) для отдельных регрессоров.

Деструктивные рыночные события (некоторые из которых показаны на рисунке 4) явно повлияли на прибыльность регрессоров. Регрессоры обучались на исторических данных, поэтому, когда рынок демонстрировал внезапные изменения, которые не были замечены в обучающих данных, регрессоры работали плохо. Это, однако, ожидаемое поведение, поскольку ни один метод машинного обучения не может предсказать поведение, которое не было включено в данные, используемые для обучения. Вместо этого ключевым моментом здесь является адаптивность. Поскольку ранее невидимые данные включаются в следующее обучение регрессоров, они теперь проникнуты новыми знаниями и должны быть в состоянии лучше справляться с подобными событиями, происходящими в будущем. Одним из ярких примеров этого является событие F на рисунке 4: события A-E представляют собой ранее невидимые или нечастые изменения и, следовательно, приводят к плохой производительности регрессора. Однако после того, как достаточное количество этих событий было включено в набор обучающих данных, регрессорам (особенно XGBoost) удалось вместо этого воспользоваться событием F.

Рисунок 4. Совокупная прибыль нормализовалась к общему размеру кошелька на протяжении всего эксперимента для каждого из двух регрессоров. Цена закрытия BTC / USDT показана в фоновом режиме, чтобы дать некоторое представление о рыночных условиях относительно производительности регрессоров. Имейте в виду, что регрессоры торговались по 19 монет каждая, многие из которых лишь слабо коррелируют с BTC. Некоторые разрушительные рыночные события (сплошные стрелки) с результирующим поведением прибыли (пунктирные стрелки) обозначены помеченными кружками. Те же рыночные события обсуждались в отношении индекса несходства на рисунке 2.

Заключение

В ходе 3-недельного эксперимента мы отслеживали сбалансированную и временную точность, а также использование ресурсов каждого регрессора для каждой монеты, а также прибыль для каждого регрессора. Имейте в виду, что оптимизация гиперпараметров для настройки регрессоров не проводилась, поэтому их производительность с точки зрения точности и прибыли следует интерпретировать только относительно друг друга. Прежде всего, этот эксперимент является доказательством концепции, демонстрирующей потенциал FreqAI для адаптивного моделирования потоковых данных в реальном времени.

Продолжающийся эксперимент

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

ОТКАЗ FreqAI не связан с какими-либо предложениями криптовалюты. FreqAI является и всегда будет некоммерческим проектом с открытым исходным кодом. У FreqAI нет крипто-токена, FreqAI не продает сигналы, а у FreqAI нет домена, кроме документации Freqtrade. Пожалуйста, остерегайтесь проектов-самозванцев и помогите нам, сообщив о них на официальный сервер FreqAI Discord.

Ссылки

1. Колк, Р. А. и другие (2022). FreqAI: обобщающее адаптивное моделирование для хаотичных прогнозов рынка временных рядов. Журнал программного обеспечения с открытым исходным кодом, 7 (80), 4864, https://doi.org/10.21105/joss.04864

Источник