В этой статье будет показано, как использование модели K-ближайших соседей в стратегии возврата к среднему может улучшить ее производительность. Кроме того, это будет проверено на истории, чтобы обеспечить показатель производительности. Это часть 5 из 5 в серии Алгоритмы машинного обучения на рынках. В конце этой серии методы машинного обучения будут сравниваться друг с другом.
Введение
Основная идея алгоритма k-NN заключается в том, что подобные вещи находятся рядом друг с другом. Когда требуется прогноз для невидимой точки данных, алгоритм просматривает «k» ближайших точек данных или «k» ближайших соседей в обучающем наборе и присваивает значение или класс на основе этих соседей.
Он работает с помощью двух известных методов:
- Классификация. Для заданной точки данных, которую необходимо классифицировать, алгоритм идентифицирует k ближайших соседей из обучающей выборки. Затем точка данных назначается классу, который имеет наибольшее количество представителей среди этих «k» соседей. Например, если k = 5 и 3 из 5 ближайших соседей относятся к классу A, а 2 — к классу B, точка данных будет классифицирована как класс A.
- Регрессия. Для задач регрессии алгоритм вычисляет среднее значение (или иногда медиану) для k ближайших соседей.
Расстояние между точками данных можно рассчитать различными способами, но наиболее распространенным методом является евклидово расстояние. Другие методы включают манхэттенское расстояние, расстояние Минковского и т. д.
Ключевым параметром алгоритма k-NN является значение «k». Если «k» слишком мало, модель может быть чрезмерно чувствительна к шуму в данных. Если «k» слишком велико, модель может включать точки, которые находятся слишком далеко и, следовательно, менее релевантны. Выбор правильного «k» часто требует проб и ошибок или таких методов, как перекрестная проверка.
Алгоритм k in k-Nearest Neighbours (k-NN) — это гиперпараметр, который вы выбираете, и он представляет количество ближайших соседей, которые следует учитывать при прогнозировании. k может принимать любое целочисленное значение, от 1 до общего количества наблюдений в вашем обучающем наборе.
Именно по той причине, что построена kNN, это не алгоритм «прогнозирования», поскольку он берет точку данных, которая уже возникла, а затем классифицирует ее. Поэтому в этом случае я решил, что kNN будет использоваться для определения того, находятся ли данные (цена) в нисходящем или восходящем тренде, а затем соответственно разместить сделку. Теоретически это будет работать следующим образом:
Для заданного интервала для прогнозирования цены акции найдите k интервалов в обучающем наборе, которые имеют наиболее схожие значения характеристик с заданным интервалом. Затем возьмите среднее (для регрессии) направление акций на этих интервалах «k» в качестве прогнозируемого движения для интервала прогнозирования.
import yfinance as yf import pandas as pd from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score, confusion_matrix from sklearn.preprocessing import StandardScaler import numpy as np # Download the data tesla = yf.download('TSLA', start='2023-04-14', end='2023-04-28', interval='15m') # Create labels: 1 if the price increased in the next interval, 0 if it decreased or stayed the same tesla['Direction'] = np.where(tesla['Close'].shift(-1) > tesla['Close'], 1, 0) # Define the features and target features = tesla.drop(['Direction', 'Close'], axis=1) # removing Close as it directly influences the label target = tesla['Direction'] # Normalize the features scaler = StandardScaler() features = scaler.fit_transform(features) # Split the data into a training set and a test set features_train, features_test, target_train, target_test = train_test_split(features[:-1], target[:-1], test_size=0.2, random_state=42) # Test different k values for k in range(1, 20): model = KNeighborsClassifier(n_neighbors=k) model.fit(features_train, target_train) predictions = model.predict(features_test) accuracy = accuracy_score(target_test, predictions) print(f"For k = {k}, accuracy = {accuracy}")
Для k = 1 точность = 0,5961538461538461
Для k = 2 точность = 0,5
Для k = 3 точность = 0,4230769230769231
Для k = 4 , точность = 0,4807692307692308
Для k = 5, точность = 0,46153846153846156
Для k = 6, точность = 0,5192307692307693
Для k = 7, точность = 0,5384615384615384
Для k = 8, точность = 0,5961538461538461
Для k = 9, точность = 0,5961538461538461
Для k = 10, точность = 0,5769230769230769
Для k = 11, точность = 0,519230769230769 3
Для k = 12, точность = 0,5192307692307693
Для k = 13, точность = 0,5384615384615384
Для k = 14, точность = 0,5961538461538461
Для k = 15, точность = 0,53846153 84615384
Для k = 16 точность = 0,5576923076923077
Для k = 17 точность = 0,5
Для k = 18 точность = 0,5192307692307693
Для k = 19 точность = 0,5
Этот сценарий сначала загружает данные из Yahoo Finance. Затем он создает метку, которая указывает, пошла ли цена вверх или вниз в следующем интервале. Он нормализует функции, случайным образом разбивает данные на обучающий набор и тестовый набор, а затем проверяет различные значения «k», чтобы определить, какое из них дает наибольшую точность на тестовый набор.
import matplotlib.pyplot as plt # Choose the best 'k' based on previous step best_k = 1 # Retrain the model using the best 'k' model = KNeighborsClassifier(n_neighbors=best_k) model.fit(features_train, target_train) # Predict the direction for all data points tesla['Predicted Direction'] = model.predict(features) # Calculate returns from the strategy tesla['Return'] = np.where(tesla['Predicted Direction'].shift() == 1, tesla['Close'].pct_change(), 0) # Calculate cumulative returns tesla['Cumulative Return'] = (1 + tesla['Return']).cumprod() # Plot cumulative returns plt.plot(tesla['Cumulative Return']) plt.title('Cumulative Returns from k-NN Strategy') plt.xticks(rotation=90) plt.show()
Торговая стратегия включает в себя «покупку» акции, когда предсказанное направление идет вверх, и «продажу», когда предсказанное направление идет вниз. Сюжет представляет собой совокупную доходность.
Этот сюжет дает ощущение «слишком хорошо, чтобы быть правдой». Это часто является результатом переобучения модели, и в этом случае это верно для рандомизации наборов обучающих и тестовых данных, а также когда «k» очень мало, поскольку модель может быть чрезмерно чувствительна к шуму в данных. Это может закончиться «запоминанием» обучающих данных, а не обучением их обобщению.
Для графика k=9 результаты также относительно многообещающие и показывают признаки того, что модель подходит. Однако есть некоторые проблемы с конструкцией модели, которые я сейчас рассмотрю.
Вместо случайного разделения данных обучающие и тестовые наборы теперь будут создаваться с использованием жесткой точки разделения (80/20 — см. ниже). Рандомизация данных позволяет обучающим и тестовым наборам быть репрезентативными для любого общего распределения данных. Но в случае финансовых временных рядов часто имеет смысл сохранять временной порядок данных (это связано с тем, что цель часто состоит в том, чтобы предсказать будущие цены на основе прошлых цен, поэтому нет смысла тренироваться на них). данные из будущего).
# Calculate the split point split_point = int(len(features) * 0.8)
Для k = 1 точность = 0,6153846153846154
Для k = 2 точность = 0,5384615384615384
Для k = 3 точность = 0,5192307692307693
Для k = 4 , точность = 0,4807692307692308
Для k = 5 точность = 0,5769230769230769
Для k = 6 точность = 0,5384615384615384
Для k = 7 точность = 0,576923076923 0769
Для k = 8 точность = 0,5192307692307693
Для k = 9 точность = 0,5192307692307693
Для k = 10 точность = 0,46153846153846156
Для k = 11, точность = 0,5769230769230769
Для k = 12, точность = 0,5576923076923077
Для k = 13, точность = 0,5769230769230769
Для k = 14, точность = 0,5576923076923077
Для k = 15 точность = 0,5769230769230769
Для k = 16 точность = 0,5769230769230769
Для k = 17 точность = 0,5769230769230769
> Для к = 18, точность = 0,5961538461538461
Для k = 19 точность = 0,5769230769230769
Даже при жестком разделении данных для обучающего и тестового наборов отдача от этой стратегии также весьма многообещающая; с ограниченным снижением прогноза.
Добавление kNN в стратегию возврата к среднему
import yfinance as yf import pandas as pd import numpy as np from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler # Download the data tesla = yf.download('TSLA', start='2023-04-14', end='2023-04-28', interval='15m') # Define the moving average window window = 15 # Calculate the moving average tesla['Moving Average'] = tesla['Close'].rolling(window=window).mean() # Create labels: 1 if the price is below the moving average (expecting it to rise towards the mean), # 0 if it is above (expecting it to fall towards the mean) tesla['Below Moving Average'] = np.where(tesla['Close'] < tesla['Moving Average'], 1, 0) # Define the features and target features = tesla.drop(['Below Moving Average', 'Close', 'Moving Average'], axis=1) target = tesla['Below Moving Average'] # Normalize the features scaler = StandardScaler() features = scaler.fit_transform(features) # Calculate the split point split_point = int(len(features) * 0.8) # Split the data into a training set and a test set features_train = features[:split_point] features_test = features[split_point:] target_train = target[:split_point] target_test = target[split_point:] # Test different k values for k in range(1, 20): model = KNeighborsClassifier(n_neighbors=k) model.fit(features_train, target_train) predictions = model.predict(features_test) accuracy = accuracy_score(target_test, predictions) print(f"For k = {k}, accuracy = {accuracy}")
В этом скрипте рассчитывается скользящая средняя цены закрытия за последние 15 интервалов. Затем определяется новая целевая переменная «Ниже скользящей средней»; что равно 1, если текущая цена ниже скользящей средней, и 0, если она выше.
Характеристики нормализуются, данные разбиваются на обучающие и тестовые наборы, а классификатор k-ближайших соседей тестируется на обучающих данных для разных значения к. Для каждого k мы вычисляем точность модели на тестовых данных.
Для k = 1 точность = 0,5192307692307693
Для k = 2 точность = 0,5192307692307693
Для k = 3 точность = 0,4807692307692308
Для k = 4 , точность = 0,4807692307692308
Для k = 5 точность = 0,46153846153846156
Для k = 6 точность = 0,5192307692307693
Для k = 7 точность = 0,46153846153846 156
Для k = 8 точность = 0,46153846153846156
Для k = 9 точность = 0,46153846153846156
Для k = 10 точность = 0,46153846153846156
Для k = 11 точность = 0,461538461538 46156
Для k = 12, точность = 0,46153846153846156
Для k = 13, точность = 0,46153846153846156
Для k = 14, точность = 0,46153846153846156
Для k = 15, точность = 0,461538461538 46156
Для к = 16, точность = 0,46153846153846156
Для k = 17, точность = 0,46153846153846156
Для k = 18, точность = 0,46153846153846156
Для k = 19, точность = 0,46153846153846 156
import matplotlib.pyplot as plt # Test different k values and choose the one with the highest accuracy accuracies = [] for k in range(1, 20): model = KNeighborsClassifier(n_neighbors=k) model.fit(features_train, target_train) predictions = model.predict(features_test) accuracy = accuracy_score(target_test, predictions) accuracies.append(accuracy) best_k = accuracies.index(max(accuracies)) + 1 # Train the model with the best k model = KNeighborsClassifier(n_neighbors=best_k) model.fit(features_train, target_train) # Predict on the test set predictions = model.predict(features_test) # Create a DataFrame for calculating returns test_data = tesla.iloc[split_point:] test_data['Predictions'] = predictions # Calculate returns test_data['Returns'] = test_data['Close'].pct_change() test_data['Strategy Returns'] = test_data['Returns'] * test_data['Predictions'].shift() # Calculate cumulative returns test_data['Cumulative Market Returns'] = (test_data['Returns'] + 1).cumprod() test_data['Cumulative Strategy Returns'] = (test_data['Strategy Returns'] + 1).cumprod() # Plot the cumulative returns plt.figure(figsize=(10,5)) plt.plot(test_data['Cumulative Market Returns'], color='blue', label='Market Returns') plt.plot(test_data['Cumulative Strategy Returns'], color='green', label='Strategy Returns') plt.title('Cumulative Returns') plt.legend() plt.show()
График показывает, что когда эта конкретная модель применяется к стратегии возврата к среднему, она не работает особенно хорошо. При этом существует большой флетовый период, когда рынок закрыт, и поэтому весь потенциал этой стратегии не виден. Более крупный набор данных действительно лучше подходит для определения эффективности этой стратегии.
Дополнительные ограничения
Как следует из максимального k, в этой модели всего 19 точек обучающих данных. Это, вероятно, ограничит воспроизводимость модели и изменит ее производительность для разных периодов.
Обратите внимание, что это очень простая модель и может не подходить для реальной торговли. В частности, не учитываются транзакционные издержки, влияние решений модели на рынок или риск, связанный с различными решениями. Он также не имеет механизма для обработки отсутствующих данных или выбросов. Наконец, он использует очень простой набор функций и не включает никакой информации о более широком рынке или других потенциально важных факторах.
В коде используется стандартный скалер, учитывающий весь набор данных, прежде чем он будет разделен на обучающую и тестовую части. наборы. Это означает, что тестовый набор изменяется всем набором данных и поэтому может рассматриваться как второстепенный источник переобучения.