Модели искусственного интеллекта для трейдинга

Лицензионное изображение из Adobe Stock

Машинное обучение и его связь с искусственным интеллектом

Машинное обучение (ML) — это разновидность искусственного интеллекта (ИИ), которая фокусируется на разработке алгоритмов, позволяющих компьютерам учиться и принимать решения на основе данных. Вместо того, чтобы быть явно запрограммированными для выполнения задачи, алгоритмы машинного обучения строят модель на основе входных данных выборки, чтобы делать прогнозы или принимать решения без вмешательства человека. Этот процесс обучения включает в себя использование статистических методов для выявления закономерностей и взаимосвязей в данных, тем самым позволяя машине со временем улучшать свою производительность с большим количеством данных.

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

Модель случайного леса в торговом техническом анализе

Я уже писал о многих моделях и методах искусственного интеллекта и машинного обучения, которые можно использовать в торговле и на финансовых рынках. Моя последняя статья «Обучение с подкреплением ИИ с помощью тренажерного зала OpenAI» может представлять интерес. Я также рекомендую заглянуть на страницу EODHD API на Medium. Я использую их API-интерфейсы для предоставления финансовых данных для обучения своих моделей. Он очень прост в использовании, и я также написал для них библиотеку Python, которая упрощает извлечение данных.

В этой статье я хочу представить и продемонстрировать модель Random Forest. Модель представляет собой метод обучения, используемый для задач классификации и регрессии. Он работает путем построения нескольких деревьев решений во время обучения и вывода режима классов (для классификации) или прогнозирования среднего (для регрессии) отдельных деревьев. Ансамбль деревьев (лес) снижает риск переобучения обучающим данным, обеспечивая надежные и точные прогнозы.

В торговом техническом анализе модель Random Forest может быть особенно полезна из-за ее способности обрабатывать большие объемы данных и сложные паттерны. Например, трейдер может использовать Random Forest для прогнозирования движения цен на акции на основе исторических данных о ценах, объеме и других технических индикаторах, таких как скользящие средние и индекс относительной силы (RSI). Обучая модель на исторических данных, она может изучить сложные взаимосвязи между этими индикаторами и будущими движениями цены.

Например, предположим, что трейдер использует набор данных, содержащий ежедневные цены на акции или криптовалюты, объем и технические индикаторы за последние пять лет. Модель Random Forest можно обучить предсказывать вероятность роста или понижения цены на следующий день. Вводя данные текущего дня, модель предоставляет вероятность, которая может помочь трейдеру принять решение о покупке или продаже, потенциально улучшая результаты торговли за счет использования возможностей модели распознавания паттернов. Этот метод не только повышает точность прогнозирования, но и помогает в управлении рисками, обеспечивая вероятностную оценку будущих движений цен.

Давайте рассмотрим практический пример…

Первым шагом нам нужно будет получить некоторые данные для работы. Ради интереса я буду использовать ежедневные данные Биткоина. Что мне нравится в EODHD API, так это то, что они быстрые, практически без ограничений на извлечение. Приведенный ниже код извлекает данные за 1999 дней.

from eodhd import APIClient
import config as cfg

api = APIClient(cfg.API_KEY)


def get_ohlc_data():
# df = api.get_historical_data("GSPC.INDX", "d", results=2000)
df = api.get_historical_data("BTC-USD.CC", "d", results=2000)
return df

if __name__ == "__main__":
df = get_ohlc_data()
print(df)
Скриншот автора

Что я хочу сделать сейчас, так это добавить несколько технических индикаторов. Это действительно зависит от вас и является частью удовольствия от экспериментов. Я собираюсь добавить SMA50, SMA200, MACD, RSI14 и VROC. Вы можете добавить сюда все, что вам больше нравится.

def calculate_sma(data, window):
return data.rolling(window=window).mean()


def calculate_macd(data, short_window=12, long_window=26, signal_window=9):
short_ema = data.ewm(span=short_window, adjust=False).mean()
long_ema = data.ewm(span=long_window, adjust=False).mean()
macd = short_ema - long_ema
signal_line = macd.ewm(span=signal_window, adjust=False).mean()
return macd, signal_line


def calculate_rsi(data, window=14):
delta = data.diff(1)
gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi


def calculate_vroc(volume, window=14):
vroc = ((volume.diff(window)) / volume.shift(window)) * 100
return vroc


if __name__ == "__main__":
df = get_ohlc_data()

df["sma50"] = calculate_sma(df["close"], 50)
df["sma200"] = calculate_sma(df["close"], 200)
df["macd"], df["signal"] = calculate_macd(df["close"])
df["rsi14"] = calculate_rsi(df["close"])
df["vroc14"] = calculate_vroc(df["volume"])

df.dropna(inplace=True)

print(df)
Скриншот автора

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

Нормализация и масштабирование

Если вы читали другие мои статьи, то заметите, что я почти всегда нормализую и масштабирую свои данные в диапазоне от 0 до 1. Это своего рода исключение из правил. В целом, масштабирование не является строгим требованием при использовании случайных лесов, поскольку они основаны на деревьях решений, которые не чувствительны к масштабу входных объектов. Тем не менее, масштабирование все же может быть полезным в некоторых сценариях, особенно при интеграции случайных лесов в конвейер с другими алгоритмами, требующими масштабирования. Кроме того, если вы планируете интерпретировать важность признаков, наличие масштабируемых данных иногда может сделать эти интерпретации более простыми. В этом примере я не собираюсь пропускать данные через средство масштабирования. Возможно, вы захотите это сделать, и если вы это сделаете, я объяснил, как это сделать в моих предыдущих статьях. Если вам нужна помощь, просто спрашивайте в комментариях.

Обучение модели

Обучение модели машинного обучения на самом деле очень простое и требует очень небольшого количества кода благодаря некоторым важным библиотекам. Вы захотите установить «scikit-learn» с помощью PIP.

% python3 -m pip install scikit-learn -U

Что вам нужно сделать, так это разделить данные на обучающий и тестовый наборы. Я почти всегда использую сплит 70/30 или 80/20. Здесь я буду использовать разделение 80/20.

# include these library imports at the top of your file

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

# put this in your main at the end

features = [
"open",
"high",
"low",
"volume",
"sma50",
"sma200",
"macd",
"signal",
"rsi14",
"vroc14",
]
X = df[features]
y = df["close"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)

И вы можете видеть, что форма X_train, X_test, y_train и y_test выглядит следующим образом.

print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

(1440, 10) (360, 10) (1440,) (360,)

Это все, что вам нужно сделать, чтобы соответствовать вашей модели.

rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)

Составление прогнозов

y_train_pred = rf.predict(X_train)
y_test_pred = rf.predict(X_test)

Визуализация прогнозов

Установите библиотеки «matplotlib» и «seaborn» с помощью PIP.

% python3 -m pip install matplotlib seaborn -U

Включите библиотеки в свой код.

import matplotlib.pyplot as plt
import seaborn as sns

Точечная диаграмма фактических и прогнозируемых значений

plt.figure(figsize=(14, 7))

plt.subplot(1, 2, 1)
plt.scatter(y_train, y_train_pred, alpha=0.3)
plt.xlabel("Actual Close Price (Train)")
plt.ylabel("Predicted Close Price (Train)")
plt.title("Actual vs. Predicted Close Price (Training Set)")
plt.plot([y_train.min(), y_train.max()], [y_train.min(), y_train.max()], "r--")

plt.subplot(1, 2, 2)
plt.scatter(y_test, y_test_pred, alpha=0.3)
plt.xlabel("Actual Close Price (Test)")
plt.ylabel("Predicted Close Price (Test)")
plt.title("Actual vs. Predicted Close Price (Testing Set)")
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "r--")

plt.tight_layout()
plt.show()
Скриншот автора

Линейный график фактических и прогнозируемых значений с течением времени

plt.figure(figsize=(14, 7))

plt.plot(y_test.index, y_test, label="Actual Close Price")
plt.plot(y_test.index, y_test_pred, label="Predicted Close Price")
plt.xlabel("Date")
plt.ylabel("Close Price")
plt.title("Actual vs. Predicted Close Price Over Time (Testing Set)")
plt.legend()
plt.show()
Скриншот автора

Оценка производительности модели

Важной задачей при работе с любой моделью AI/ML является оценка производительности. Это может быть очень полезно при сравнении моделей. Их может быть больше, но я всегда использовал среднюю абсолютную ошибку (MAE), среднеквадратичную ошибку (MSE) и R-квадрат (R²). Они, кажется, самые распространенные.

train_mae = mean_absolute_error(y_train, y_train_pred)
test_mae = mean_absolute_error(y_test, y_test_pred)
train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)

print(f"Training MAE: {train_mae}")
print(f"Testing MAE: {test_mae}")
print(f"Training MSE: {train_mse}")
print(f"Testing MSE: {test_mse}")
print(f"Training R²: {train_r2}")
print(f"Testing R²: {test_r2}")

Результат для моей модели выглядит следующим образом:

Training MAE: 149.95774584583577
Testing MAE: 375.66243670875343
Training MSE: 59806.0910378797
Testing MSE: 402962.34869884106
Training R²: 0.9998008096169744
Testing R²: 0.9987438463433689

Средняя абсолютная ошибка (MAE):

  • Тренировочный MAE: 149.96
  • Тестирование MAE: 375.66

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

  • Более низкое значение MAE указывает на лучшую производительность модели.
  • Здесь Обучающий MAE значительно ниже, чем Тестовый MAE, что говорит о том, что модель лучше работает на обучающих данных по сравнению с проверочными данными.

Среднеквадратичная ошибка (MSE):

  • Обучение MSE: 59806.09
  • Тестирование MSE: 402962.35

MSE измеряет среднеквадратичные ошибки между прогнозируемым и фактическим значениями. Он наказывает за большие ошибки больше, чем MAE, что делает его чувствительным к выбросам.

  • Более низкое значение MSE указывает на лучшую производительность модели.
  • Как и в случае с MAE, MSE для обучения намного ниже, чем для MSE для тестирования, что указывает на лучшую производительность при работе с данными обучения.

R-квадрат (R²):

  • Тренировочный R²: 0.9998
  • Тестирование R²: 0.9987

R² измеряет долю дисперсии зависимой переменной, которая предсказуема по независимым переменным. Он находится в диапазоне от 0 до 1, где 1 указывает на идеальный прогноз.

  • Чем выше R², тем выше производительность модели.
  • Значения R² для обучения и тестирования очень высоки и близки к 1, что указывает на то, что модель объясняет почти всю дисперсию данных как для обучающего, так и для проверочного наборов.

Так что же это означает на самом деле и почему это важно?

Модель исключительно хорошо работает на обучающих данных, о чем свидетельствуют низкие значения Training MAE и MSE и высокие Training R². Это говорит о том, что модель очень хорошо усвоила закономерности в обучающих данных.
Модель также очень хорошо показывает себя на данных тестирования, о чем свидетельствует высокий R² тестирования. Тем не менее, тестовые MAE и MSE выше по сравнению с показателями обучения. Это несоответствие указывает на некоторую степень переобучения, когда модель может улавливать шум в обучающих данных, который плохо обобщается на невидимые данные тестирования.

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

Что мы можем сделать, чтобы улучшить ситуацию?

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

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

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

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

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

Вот код, который поможет вам начать…

# update this import at the top

from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV

# modify the mode in your main

param_grid = {
"n_estimators": [50, 100, 200],
"max_depth": [10, 20, 30, None],
"min_samples_split": [2, 5, 10],
"min_samples_leaf": [1, 2, 4],
"bootstrap": [True, False],
}

rf = RandomForestRegressor(random_state=42)
grid_search = GridSearchCV(
estimator=rf, param_grid=param_grid, cv=5, n_jobs=-1, verbose=2
)
grid_search.fit(X_train, y_train)

print(f"Best parameters: {grid_search.best_params_}")

best_rf = grid_search.best_estimator_
best_rf.fit(X_train, y_train)

Вы заметите, что теперь обучение занимает гораздо больше времени. Мой iMac, который довольно мощный, звучал так, как будто он вот-вот взлетит, он работал так усердно 🙂

Best parameters: {'bootstrap': True, 'max_depth': 10, 'min_samples_leaf': 2, 'min_samples_split': 5, 'n_estimators': 200}

Training MAE: 185.84993192666315
Testing MAE: 370.55759716881033
Training MSE: 91681.34913180597
Testing MSE: 404506.5183105951
Training R²: 0.9996946457671292
Testing R²: 0.9987390327067834

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

Важность функции

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

Установите библиотеку «pandas» с помощью PIP.

% python3 -m pip install pandas -U

Включите библиотеку в свой код.

import pandas as pd
feature_importances = best_rf.feature_importances_
importance_df = pd.DataFrame(
{"Feature": features, "Importance": feature_importances}
)

importance_df = importance_df.sort_values(by="Importance", ascending=False)

plt.figure(figsize=(12, 8))
sns.barplot(x="Importance", y="Feature", data=importance_df)
plt.title("Feature Importances of Technical Indicators")
plt.show()
Скриншот автора

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

Приведу несколько примеров…

  1. SMA200 и SMA50 сами по себе мало о чем говорят, но использование кроссоверов для сигналов на покупку и продажу — да. Создание функции, которая показывает, что SMA50 находится выше или ниже SMA200 и когда он пересекается, может быть лучшей функцией для подачи.
  2. RSI14 сам по себе мало о чем говорит. Если бы вы использовали правила, если RSI14 ниже 30, то покупайте или выше 70, а затем продавайте, то, возможно, это было бы лучшей функцией для отслеживания.
  3. MACD и Signal могут быть очень мощными, но при использовании кроссоверов. Когда сигнал находится выше и ниже MACD. Сами по себе они мало что вам расскажут.
  4. VROC14 также очень интересен, но опять же, вам нужно применять некоторые правила технического анализа, чтобы понять сигналы на покупку и продажу.

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

Источник