Алгоритмы машинного обучения на рынках

  • Часть 1: Использование деревьев решений для улучшения стратегии возврата к среднему в Python
  • Часть 2: Использование случайных лесов для улучшения стратегии возврата к среднему в Python
  • Часть 3: Использование машин опорных векторов для улучшения стратегии возврата к среднему в Python
  • Часть 4: Использование искусственных нейронных сетей для улучшения стратегии возврата к среднему в Python
  • Часть 4.2: Использование искусственных нейронных сетей для улучшения стратегии возврата к среднему в Python
  • Часть 5: Использование алгоритма k-ближайшего соседа для улучшения стратегии возврата к среднему в Python
  • Часть 6. Классификация временных рядов с использованием динамического искривления времени K-ближайших соседей

Часть 1: Использование деревьев решений для улучшения стратегии возврата к среднему в Python

В этой статье будет показано, как использование деревьев решений в стратегии возврата к среднему может повысить ее производительность. Кроме того, это будет протестировано на истории, чтобы обеспечить индикатор соответствия. Это часть 1 из 5 из серии «Использование алгоритмов машинного обучения на рынках». В конце этой серии каждый метод машинного обучения будет сравниваться с их аналогами.

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

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

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

Дословный пример — Это упрощенный пример того, как дерево решений может помочь стратегии возврата к среднему значению на практике:

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

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

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

  • Если объем торгов акциями низкий, алгоритм предсказывает, что цена, скорее всего, вернется к среднему значению.
  • Если объем торгов акциями высок, алгоритм проверяет волатильность акций.
  • Если волатильность акций низкая, алгоритм предсказывает, что цена, скорее всего, продолжит отклоняться от среднего значения.
  • Если волатильность акции высокая, алгоритм проверяет последние изменения цены.
  • Если недавние изменения цены отрицательны, алгоритм предсказывает, что цена, скорее всего, вернется к среднему значению.
  • Если недавние изменения цены положительные, алгоритм предсказывает, что цена, скорее всего, продолжит отклоняться от среднего значения.

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

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

Код Python для дерева решений

import yfinance as yf
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# Get Tesla stock data from yfinance
tesla = yf.Ticker("TSLA").history(interval="15m")

# Create the feature and target arrays
X = tesla[['Open', 'High', 'Low', 'Close']]
y = (tesla['Close'] > tesla['Open']).astype(int)

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# Fit the decision tree classifier
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)

# Predict on the testing data and calculate accuracy
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)

print("Accuracy:", accuracy)

Выпуск:
Точность: 0,6761904761904762

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

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

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

from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier

# Define the parameter grid to search
param_grid = {'max_depth': [2, 4, 6, 8, 10],
'min_samples_split': [2, 4, 6, 8, 10]}

# Create the classifier
clf = DecisionTreeClassifier()

# Create the grid search object
grid_search = GridSearchCV(estimator=clf, param_grid=param_grid, cv=5, scoring='accuracy')

# Fit the grid search to the data
grid_search.fit(X_train, y_train)

# Print the best parameters and accuracy score
print("Best parameters:", grid_search.best_params_)
print("Best accuracy:", grid_search.best_score_)

# Use the best performing classifier for making predictions
clf = grid_search.best_estimator_
y_pred = clf.predict(X_test)

# Calculate the accuracy of the classifier
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)

# Implement the mean reversion strategy as before
df['Prediction'] = np.nan
df.loc[X_test.index, 'Prediction'] = y_pred
df['Position'] = np.where(df['Prediction'] == 1, 1, -1)
df['Strategy'] = df['Position'] * df['Close'].pct_change()
df['Cumulative'] = df['Strategy'].cumsum()

# Plot the cumulative returns of the strategy
import matplotlib.pyplot as plt
plt.plot(df['Cumulative'])
plt.xlabel('Index')
plt.ylabel('Returns')
plt.title('Cumulative Returns of the Mean Reversion Strategy')
plt.show()

Какие выходы:
— Лучшие параметры: {‘max_depth’: 10, ‘min_samples_split’: 4}
— Лучшая точность: 0.5456999522216913
— Точность: 0.5565217391304348

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

Здесь есть несколько способов повысить эффективность стратегии возврата к среднему:

  1. Разработка функций: Текущая стратегия использует только 4 функции (Открытие, Максимум, Минимум, Закрытие) для прогнозирования. Добавление дополнительных функций, таких как технические индикаторы или экономические данные, потенциально может повысить производительность модели.
  2. Выбор модели: Классификатор дерева решений может быть не лучшей моделью для этой стратегии возврата к среднему. Использование других моделей машинного обучения, таких как случайный лес, машины опорных векторов или нейронные сети, потенциально может повысить эффективность стратегии.
  3. Ансамблевые методы: Объединение нескольких моделей с использованием ансамблевых методов, таких как упаковка в мешки или повышение, потенциально может повысить эффективность стратегии за счет объединения сильных сторон различных моделей.
  4. Тестирование на истории: Текущая стратегия использует простую оценку точности для оценки ее эффективности. Однако более комплексная структура тестирования на истории, включающая транзакционные издержки, управление рисками и несколько показателей оценки, может дать более точную картину эффективности стратегии.

Часть 2: Использование случайных лесов для улучшения стратегии возврата к среднему в Python

В этой статье будет показано, как использование Random Forests в стратегии возврата к среднему может повысить его производительность. Кроме того, это будет протестировано на истории, чтобы обеспечить индикатор соответствия. Это часть 2 из 5 из серии «Алгоритмы машинного обучения на рынках». В конце этой серии каждый метод машинного обучения будет сравниваться с их аналогами.

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

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

Случайные леса в Python
В следующем блоке кода описано, как реализовать случайный лес на биржевых данных Tesla из библиотеки yfinance:

import yfinance as yf
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split

# Download Tesla stock data from yfinance
tesla = yf.Ticker("TSLA").history(period="max")

# Select features to use in the model
features = ['Open', 'High', 'Low', 'Close']
X = tesla[features]
y = tesla['Close']

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# Train the random forest model
rf = RandomForestRegressor(n_estimators=100, random_state=0)
rf.fit(X_train, y_train)

# Evaluate the model on the test data
y_pred = rf.predict(X_test)
print('Random Forest R2:', rf.score(X_test, y_test))

Этот код загружает исторические данные об акциях Tesla из yfinance, выбирает цены «Открытие», «Максимум», «Минимум» и «Закрытие» в качестве функций для модели и разбивает данные на обучающий и тестовый наборы. Затем модель случайного леса обучается на обучающих данных с помощью класса RandomForestRegressor из библиотеки scikit-learn. Наконец, модель оценивается по тестовым данным путем вычисления оценки R2, которая измеряет долю вариации целевой переменной, которая объясняется особенностями.

Улучшение параметров случайного леса
Существует несколько способов оптимизации случайного регрессора леса:

  1. Количество деревьев: Увеличение количества деревьев в лесу, как правило, повышает производительность модели, но за счет увеличения времени вычислений. Поиск оптимального количества деревьев требует баланса между точностью и временем вычислений.
  2. Максимальная глубина деревьев: Максимальную глубину деревьев в лесу можно контролировать, установив max_depth. Увеличение максимальной глубины приведет к более сложным деревьям, что может привести к переоснащению, если установить слишком высокую высоту. С другой стороны, слишком низкая максимальная глубина может привести к недостаточной подготовке.
  3. Минимальное количество образцов на листе: параметр min_samples_leaf управляет минимальным количеством образцов, необходимых для нахождения в узле листа. Увеличение значения приведет к уменьшению количества деревьев, что может уменьшить переобучение, но также может привести к потере информации.
  4. Максимум возможностей: параметр max_features определяет максимальное количество признаков, которые можно использовать при разделении узла. Увеличение значения приведет к модели, которая учитывает больше функций, но также может привести к переобучению, если установить слишком высокую цену.
  5. Количество выборок для разделения узла: min_samples_split управляет минимальным количеством выборок, необходимых для разделения внутреннего узла. Увеличение стоимости приведет к увеличению количества деревьев, что может уменьшить переобучение.
  6. Начальная загрузка: параметр bootstrap определяет, следует ли использовать начальную загрузку при построении деревьев. Начальная загрузка включает в себя случайную выборку обучающих данных с заменой, что может привести к увеличению вариативности деревьев и повышению производительности.
  7. Оценка OOB: oob_score определяет, следует ли использовать выборки из мешка для оценки точности обобщения модели. Образцы из мешка — это образцы, которые не используются при обучении какого-либо отдельного дерева.
  8. Перекрестная проверка: Использование методов перекрестной проверки, таких как перекрестная проверка K-fold, может помочь настроить параметры случайного леса и оценить точность его обобщения.

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

import yfinance as yf
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV

# Get Tesla 15 minute interval data from yfinance
tesla = yf.download("TSLA", interval="15m")

# Prepare data for mean reversion strategy
lookback = 50 # Number of periods to look back
tesla['CloseMA'] = tesla['Close'].rolling(window=lookback).mean()
tesla['ZScore'] = (tesla['Close'] - tesla['CloseMA']) / tesla['Close'].rolling(window=lookback).std()

# Split data into train and test sets
train = tesla.iloc[lookback:int(0.8*len(tesla)), :]
test = tesla.iloc[int(0.8*len(tesla)):, :]

# Define grid of hyperparameters to test
param_grid = {
'n_estimators': [10, 50, 100, 200],
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 4, 6, 8],
'min_samples_leaf': [1, 2, 4, 6],
'max_features': [None, 'auto', 'sqrt', 'log2'],
'bootstrap': [True, False]
}

# Initialize Random Forest Regressor
rf = RandomForestRegressor()

# Conduct grid search to find best hyperparameters
grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)
grid_search.fit(train.iloc[:, :-1], train['ZScore'])

# Print the best hyperparameters
print("Best hyperparameters: ", grid_search.best_params_)

Какие выходы: Лучшие гиперпараметры: {‘bootstrap’: False, ‘max_depth’: 10, ‘max_features’: ‘auto’, ‘min_samples_leaf’: 1, ‘min_samples_split’: 2, ‘n_estimators’: 10}
Выходные данные этого фрагмента кода указывают на период ретроспективного анализа, равный 50. При изменении ретроспективный период приводит к значительным изменениям в доходах. В следующем блоке кода период ретроспективного анализа оптимизируется с помощью цикла for и различных значений периода ретроспективного анализа. Примечание: для выполнения Jupyter Notebook требуется много времени.

Реализация Стратегии
Использование оптимизированного случайного леса при возврате к среднему:

import yfinance as yf
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV
import matplotlib.pyplot as plt

# Get Tesla 15 minute interval data from yfinance
tesla = yf.Ticker("TSLA").history(interval="15m")

# Define list of different lookback periods to test
lookback_periods = [10, 25, 50, 75, 100]
returns = []

# Loop through different lookback periods
for lookback in lookback_periods:
# Prepare data for mean reversion strategy
tesla['CloseMA'] = tesla['Close'].rolling(window=lookback).mean()
tesla['ZScore'] = (tesla['Close'] - tesla['CloseMA']) / tesla['Close'].rolling(window=lookback).std()

# Split data into train and test sets
train = tesla.iloc[lookback:int(0.8*len(tesla)), :]
test = tesla.iloc[int(0.8*len(tesla)):, :]

# Define grid of hyperparameters to test
param_grid = {
'n_estimators': [10, 50, 100, 200],
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 4, 6, 8],
'min_samples_leaf': [1, 2, 4, 6],
'max_features': [None, 'auto', 'sqrt', 'log2'],
'bootstrap': [True, False]
}

# Initialize Random Forest Regressor
rf = RandomForestRegressor()

# Conduct grid search to find best hyperparameters
grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)
grid_search.fit(train.iloc[:, :-1], train['ZScore'])

# Use the best hyperparameters to fit the random forest on the entire train data
best_rf = RandomForestRegressor(**grid_search.best_params_)
best_rf.fit(train.iloc[:, :-1], train['ZScore'])

# Use the fitted random forest to predict z-scores on the test data
test['PredZScore'] = best_rf.predict(test.iloc[:, :-1])

# Calculate returns based on mean reversion strategy
test['Return'] = np.where(test['PredZScore'] > 0, (test['CloseMA'] + test['Close'].rolling(window=lookback).std()) / test['Close'],
(test['CloseMA'] - test['Close'].rolling(window=lookback).std()) / test['Close']) - 1

# Store the cumulative returns for each lookback period
returns.append(test['Return'].cumsum())

# Plot thethe returns for each lookback period

plt.figure(figsize=(10,7))
for i, ret in enumerate(returns):
plt.plot(ret, label=f'Lookback period: {lookback_periods[i]}')
plt.legend()
plt.xlabel('Time')
plt.ylabel('Cumulative Returns')
plt.title('Cumulative Returns for Different Lookback Periods')
plt.show()

В этом коде используются наилучшие гиперпараметры, полученные в результате поиска по сетке, чтобы подогнать случайный регрессор леса ко всем данным поезда. Затем он использует подогнанный случайный лес для прогнозирования z-баллов по тестовым данным и вычисляет доходность на основе стратегии возврата к среднему. Доходность строится с помощью matplotlib.
Период ретроспективного анализа в стратегии возврата к среднему относится к количеству исторических периодов времени, используемых для расчета скользящей средней и стандартного отклонения для каждой цены акции. Эти скользящие средние и стандартные отклонения затем используются для определения того, переоценена или недооценена акция по сравнению с ее историческим поведением. Период ретроспективного анализа может повлиять на результаты стратегии, поэтому его часто тестируют на разные значения, чтобы определить оптимальный период. В данном случае это количество исторических периодов времени для 15-метрового интервала, используемого для расчета MA и SD для Tesla.

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


# Extract the start and end dates from the data
start_date = test['Return'].index[0].strftime("%Y-%m-%d %H:%M:%S")
end_date = test['Return'].index[-1].strftime("%Y-%m-%d %H:%M:%S")


print("Start date:", start_date)
print("End date:", end_date)

tesla_data = tesla[start_date:end_date]
plt.plot(tesla_data'Close'])
plt.xlabel('Date')
plt.ylabel('Close Price')
plt.title('Tesla Close Price (' + start_date + ' to ' + end_date + ')')
plt.show()

Независимо от этого, производительность/доходность этого ML для стратегии возврата к среднему в целом намного лучше, чем метод дерева решений в части 1. Стоит отметить, что данные о ценах Tesla находятся в восходящем тренде, и нет большого количества волатильности или нисходящих движений. Таким образом, больший набор данных с такими условиями будет лучшим представлением эффективности этих стратегий (к сожалению, одним из неизбежных недостатков использования yfinance является то, что он допускает только 60-дневный исторический период для 15-месячных интервалов данных об акциях).

Часть 3: Использование машин опорных векторов для улучшения стратегии возврата к среднему в Python

В этой статье будет показано, как использование машин опорных векторов в стратегии возврата к среднему может улучшить ее производительность. Кроме того, это будет протестировано на истории, чтобы обеспечить индикатор соответствия. Это часть 2 из 5 из серии «Алгоритмы машинного обучения на рынках». В конце этой серии каждый метод машинного обучения будет сравниваться с их аналогами.

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

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

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

import yfinance as yf
import pandas as pd
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split

# Get the 15-minute Tesla data from yfinance
tesla_data = yf.Ticker("TSLA").history(interval="15m")

# Convert the data into a pandas dataframe
df = pd.DataFrame(tesla_data)

# Calculate the mean of the Close prices
mean = df["Close"].mean()

# Prepare the data for training
X = df.drop("Close", axis=1).values
y = df["Close"].values

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# Train the SVR model
clf = SVR()
clf.fit(X_train, y_train)


# Predict the Close values using the test data
y_pred = clf.predict(X_test)

# Evaluate the accuracy of the model
print("Predicted Close values:", y_pred)

import matplotlib.pyplot as plt

# Predict the Close values using the test data
y_pred = clf.predict(X_test)

# Plot the observed and predicted Close prices
plt.plot(y_test, label="Observed Close Prices")
plt.plot(y_pred, label="Predicted Close Prices")

# Add a legend and labels
plt.legend()
plt.xlabel("Index")
plt.ylabel("Close Price")

# Show the plot
plt.show()

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

Реализация метода опорных векторов со стратегией возврата к среднему значению
Высокоуровневая стратегия реализации торговой стратегии возврата к среднему с помощью машины опорных векторов (SVM) для 15-минутных данных Tesla от yfinance может быть структурирована следующим образом:

  1. Подготовка данных:
  • Получите исторические 15-минутные данные для Tesla из yfinance.
  • Рассчитайте среднюю цену (среднее значение цены покупки и продажи) и получите доходность.
  • Подготовьте данные, разделив их на обучающий и тестовый наборы.

2. Модельное обучение:

  • Используйте алгоритм SVM для обучения модели на помеченных данных.

3. Тестирование модели I:

  • Протестируйте модель на наборе тестовых данных для получения прогнозов.
  • Реализуйте стратегию возврата к среднему значению на прогнозах.

4. Оцените производительность:

  • Для зацикливания стратегии получить столбец доходности.
  • Распечатайте отчеты и создайте график.

— — — — — — — — — — — — — — —
Дальнейшие шаги, которые можно предпринять:

5. Тестирование на истории:

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

6. Реализация торговли:

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

SVM и стратегия возврата к среднему значению в Python

Этот блок кода реализует всю стратегию.

import yfinance as yf
import pandas as pd
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt

# Get the 15-minute Tesla data from yfinance
tesla_data = yf.Ticker("TSLA").history(interval="15m")

# Convert the data into a pandas dataframe
df = pd.DataFrame(tesla_data)

# Calculate the mean of the Close prices
mean = df["Close"].mean()

# Prepare the data for training
X = df.drop("Close", axis=1).values
y = df["Close"].values

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# Train the SVR model
clf = SVR()
clf.fit(X_train, y_train)

# Predict the Close values using the test data
y_pred = clf.predict(X_test)

# Implement the mean reversion strategy
position = 0 # 0 means no position, 1 means long, -1 means short
for i in range(len(y_pred)):
if position == 0:
if y_pred[i] > mean:
position = 1 # go long
elif y_pred[i] < mean:
position = -1 # go short
elif position == 1:
if y_pred[i] < mean:
position = 0 # exit long
elif position == -1:
if y_pred[i] > mean:
position = 0 # exit short

# Evaluate the performance of the strategy
returns = np.zeros(len(y_pred))
for i in range(1, len(y_pred)):
if position == 1:
returns[i] = y_pred[i] - y_pred[i-1]
elif position == -1:
returns[i] = y_pred[i-1] - y_pred[i]

print("Total returns:", sum(returns))

# Plot the returns
plt.plot(returns)
plt.title("Returns for the Mean Reversion Strategy")
plt.xlabel("Time (Index)")
plt.ylabel("Return")
plt.xticks(rotation=90) # Rotate the x-axis labels to prevent overlap
plt.show()

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

Есть несколько способов улучшить эту стратегию:

  1. Feature Engineering: Повышение качества и актуальности функций, используемых для обучения модели SVM, может привести к улучшению прогнозов и повышению точности.
  2. Настройка гиперпараметров: Модель SVM имеет несколько гиперпараметров, которые можно настроить для оптимизации производительности модели. Это можно сделать с помощью таких методов, как поиск по сетке и случайный поиск (*см. ниже).
  3. Ансамблевые методы: Объединение нескольких моделей SVM или интеграция модели SVM с другими моделями может повысить общую точность торговой стратегии.
  4. Использование нелинейных моделей SVM: В некоторых случаях линейная модель SVM может не подходить для заданных данных. Использование нелинейной модели SVM, такой как SVM с радиальной базисной функцией (RBF), может дать лучшие результаты.
  5. Включение рыночной информации: Добавление в торговую стратегию дополнительной рыночной информации, такой как технические индикаторы, экономические данные или новостные настроения, может дать ценную информацию и повысить производительность модели SVM.
  6. Валидация модели: Обеспечение надежности модели SVM и ее обобщения на невидимые данные имеет решающее значение для успешной торговой стратегии. Это можно сделать с помощью таких методов, как перекрестная проверка и тестирование вне выборки.

Важные гиперпараметры, которые могут быть изменены:

  1. C: Гиперпараметр C контролирует компромисс между получением правильной классификации и максимально возможным запасом. Меньшее значение C приведет к более широкому диапазону, в то время как большее значение C приведет к меньшему запасу.
  2. kernel: Гиперпараметр ядра определяет тип функции, используемой для отображения данных в пространстве более высокой размерности. Общие функции ядра включают линейные, полиномиальные и радиальные базисные функции (RBF).
  3. gamma: гиперпараметр gamma управляет формой ядра RBF. Небольшое значение гамма-излучения приведет к широкому ядру RBF, в то время как большое значение гамма-излучения приведет к узкому ядру RBF.
  4. степень: Гиперпараметр степени определяет степень функции полиномиального ядра.
  5. epsilon: гиперпараметр epsilon используется в функции потерь, нечувствительных к эпсилону, в регрессии SVM. Он определяет допуск на отклонение от целевых значений.

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

Часть 4: Использование искусственных нейронных сетей для улучшения стратегии возврата к среднему в Python

В этой статье будет показано, как использование искусственных нейронных сетей в стратегии возврата к среднему может улучшить ее производительность. Кроме того, это будет протестировано на истории, чтобы обеспечить индикатор соответствия. Это часть 4i из 5 в серии «Алгоритмы машинного обучения на рынках». В конце этой серии каждый метод машинного обучения будет сравниваться с их аналогами.

Я понял, что код для ИНС (в частности, LSTM в этой статье) значительно длиннее, чем в предыдущих методах машинного обучения, поэтому я разделил часть 4 серии на более чем 5 компонентов, в зависимости от того, насколько длинны другие статьи о ИНС.

Обратите внимание: часть кода, сгенерированного в этой статье, требует большого кэша памяти в операционной системе Python, обычно я запускаю эти алгоритмы через Jupyter Notebook, однако ядро постоянно умирает при запуске ANN, поскольку они требуют значительно больше вычислительной памяти. В данном случае я просто перешел на Google Colab (но в нашем распоряжении есть много других вариантов). Кроме того, я считаю, что стоит упомянуть, что эта статья заметно длиннее, чем предыдущая в серии машинного обучения, что связано с тем, что ИНС сами по себе являются целой категорией потенциальных алгоритмов. Возможно, в будущем я более подробно расскажу о каждой ИНС.

Знакомство
Искусственные нейронные сети (ИНС) — это вычислительные модели, вдохновленные структурой и функциями человеческого мозга. Они состоят из взаимосвязанных узлов, называемых искусственными нейронами, которые обрабатывают и передают информацию друг другу. ИНС обучены распознавать закономерности во входных данных и могут использоваться для выполнения различных задач, таких как классификация, регрессия и прогнозирование.

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

Искусственные нейронные сети (ИНС) могут быть использованы для улучшения торговой стратегии возврата к среднему несколькими способами:

  1. Извлечение признаков: ИНС можно использовать для извлечения соответствующих признаков из необработанных данных и преобразования их в более полезное представление для принятия торговых решений. Например, сеть может быть обучена выявлять закономерности в данных, указывающие на возврат к среднему значению.
  2. Прогнозное моделирование: ИНС можно использовать для прогнозирования будущих движений цен на основе исторических данных. Сеть может быть обучена на прошлых ценовых данных для выявления условий, которые приводят к возврату к среднему значению, а затем использована для прогнозирования будущих событий возврата к среднему.
  3. Оптимизация портфеля: ИНС можно использовать для оптимизации торгового портфеля путем выбора наиболее подходящих акций для стратегии возврата к среднему. Сеть может быть обучена на исторических данных, чтобы определить акции, которые, скорее всего, продемонстрируют поведение возврата к среднему, а затем использоваться для выбора этих акций для включения в портфель.
  4. Управление рисками: ИНС можно использовать для управления рисками путем предоставления сигналов раннего предупреждения, когда возврат к среднему значению маловерсован, что позволяет трейдерам соответствующим образом корректировать свои позиции. Сеть может быть обучена на исторических данных, чтобы определить условия, связанные с повышенным риском, а затем использоваться для прогнозирования будущих уровней риска.

Эта статья будет посвящена прогнозному моделированию; то есть, может ли использование ИНС улучшить прогностическую эффективность стратегии возврата к среднему. Используемые ИНС являются одними из самых популярных и богатых в литературе:

  1. Нейронные сети с прямой связью: это самый простой тип ИНС, который состоит из входного слоя, одного или нескольких скрытых слоев и выходного слоя. Нейронные сети с прямой связью хорошо подходят для задач регрессии, где цель состоит в том, чтобы предсказать непрерывное выходное значение (например, цены на акции) на основе входных данных.
  2. Рекуррентные нейронные сети (RNN): RNN предназначены для обработки данных временных рядов, таких как движения цен на акции с течением времени. Они особенно полезны для моделирования последовательностей событий, когда выходные данные одного события возвращаются в сеть в качестве входных данных для следующего события. RNN можно использовать для фиксации закономерностей в ценах на акции, которые сохраняются с течением времени и связаны с возвратом к среднему значению.
  3. Сверточные нейронные сети (CNN): CNN предназначены для обработки данных изображений, но их также можно использовать для данных временных рядов. Они особенно полезны для анализа закономерностей в финансовых данных, которые сохраняются с течением времени, но они также хорошо подходят для выявления закономерностей в ценах на акции, связанных с возвратом к среднему значению.
  4. Сети с длинной краткосрочной памятью (LSTM): LSTM — это тип RNN, предназначенный для обработки долгосрочных зависимостей в данных временных рядов. Они особенно полезны для задач финансового прогнозирования, где цель состоит в том, чтобы предсказать будущие цены на акции на основе исторических данных. LSTM можно использовать для выявления закономерностей в ценах на акции, которые сохраняются в течение длительных периодов времени и связаны с возвратом к среднему значению.

Сети долговременной кратковременной памяти.
Как упоминалось в их кратком очерке выше, LSTM являются полезными прогностическими инструментами для данных временных рядов. Возможно, неудивительно, что, учитывая их название, они особенно лучше прогнозируют в течение более длительных периодов времени, чем их коллеги, из-за их конструкции, позволяющей им хранить краткосрочные шаблоны. На рисунке 3 представлена серия мероприятий, которые проводит LSTM; Четкое описание того, как блок памяти является рекурсивным, а ворота забвения устраняют нежелательную информацию.

Сначала я использовал LSTM сам по себе для прогнозирования данных Tesla.

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, LSTM, Dropout
from keras.optimizers import Adam

# Download 15-minute Tesla data from yfinance
tesla = yf.Ticker('TSLA')
data = tesla.history(interval='15m')

# Reset the index to make 'Date' a regular column
data = data.reset_index()

# Preprocess the data
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data['Close'].values.reshape(-1, 1))

# Create training and testing datasets
def create_dataset(scaled_data, lookback=60):
X, y = [], []
for i in range(lookback, len(scaled_data)):
X.append(scaled_data[i - lookback:i, 0])
y.append(scaled_data[i, 0])
return np.array(X), np.array(y)

lookback = 60
train_ratio = 0.8
train_size = int(len(scaled_data) * train_ratio)

train_data = scaled_data[:train_size]
test_data = scaled_data[train_size - lookback:]

X_train, y_train = create_dataset(train_data, lookback)
X_test, y_test = create_dataset(test_data, lookback)

X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))

# Build the LSTM model
model = Sequential()
model.add(LSTM(50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(Dropout(0.2))
model.add(LSTM(50, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(50))
model.add(Dropout(0.2))
model.add(Dense(1))

model.compile(optimizer=Adam(lr=0.001), loss='mean_squared_error')

# Train the model
model.fit(X_train, y_train, epochs=100, batch_size=64)

# Make predictions
predicted_prices = model.predict(X_test)
predicted_prices = scaler.inverse_transform(predicted_prices)

# Evaluate the model
rmse = np.sqrt(np.mean((predicted_prices - y_test)**2))
print('Root Mean Squared Error:', rmse)

# Plotting the predicted against the actual
# Calculate the number of test data points
num_test_points = len(data) - (train_size + lookback)

# Inverse transform the actual prices (y_test)
actual_prices = scaler.inverse_transform(y_test.reshape(-1, 1))

# Plot actual and predicted stock prices
plt.figure(figsize=(14, 6))
plt.plot(data.iloc[-num_test_points:, 0], actual_prices[:num_test_points], label='Actual Prices')
plt.plot(data.iloc[-num_test_points:, 0], predicted_prices[:num_test_points, 0], label='Predicted Prices')
plt.xlabel('Datetime')
plt.ylabel('Stock Prices')
plt.title('Tesla Stock Price Prediction')
plt.legend()
plt.show()

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

# Calculate the number of test data points
num_test_points = len(data) - (train_size + lookback)

# Generate trading signals: buy when predicted price > actual price, sell when predicted price < actual price
signals = np.where(predicted_prices[:num_test_points, 0] > actual_prices[:num_test_points, 0], 1, -1)

# Calculate daily returns
returns = np.diff(actual_prices[:num_test_points].reshape(-1), axis=0) * signals[:-1]

# Calculate the cumulative returns
cumulative_returns = np.cumsum(returns)

# Plot the returns
plt.figure(figsize=(14, 6))
plt.plot(data.iloc[-len(returns):, 0].values, cumulative_returns, label='Strategy Returns')
plt.xlabel('Datetime')
plt.ylabel('Cumulative Returns')
plt.title('Tesla Trading Strategy Returns')
plt.legend()
plt.show()

Вообще говоря, этот график находится на спаде, когда цены относительно стабильны или растут. Однако, когда цена падает, стратегия работает лучше с положительной доходностью. Это, конечно, только для указанного 15-минутного интервала, разумно предположить, что если бы это было сделано дальше, отдача была бы более отрицательной. Написав статью о LSTM, я знаю причину, по которой это может иметь место:
— Исторические данные о ценах, используемые отдельно без каких-либо дополнительных индикаторов, часто приводят к низкой производительности. Это связано со стохастическим (случайным) характером финансовых рынков. Что может улучшить производительность автономного LSTM, так это включение объема и, возможно, MACD (намек на будущие статьи).

Теперь объединим LSTM со стратегией возврата к среднему значению (Примечание: если число эпох велико, выполнение кода занимает несколько минут).

import yfinance as yf
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout

# Download 15-minute Tesla data from yfinance
tesla = yf.Ticker('TSLA')
tesla = tesla.history(interval='15m')

# Reset the index and rename the columns
tesla.reset_index(inplace=True)
tesla.columns = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Dividends','Stock Splits', 'Volume']


# Preprocess data
data = tesla[['Close']]
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data)


# Create a Function that generates 'good' data for the process
def create_dataset(data, lookback):
features, targets = [], []
for i in range(lookback, len(data)):
features.append(data[i-lookback:i])
targets.append(data[i, 0])
return np.array(features), np.array(targets)

lookback = 60
mean_reversion_features, mean_reversion_targets = create_dataset(scaled_data, lookback)

# Split data into train and test
train_size = int(len(mean_reversion_features) * 0.8)

X_train_mean_reversion, y_train_mean_reversion = mean_reversion_features[:train_size], mean_reversion_targets[:train_size]
X_test_mean_reversion, y_test_mean_reversion = mean_reversion_features[train_size:], mean_reversion_targets[train_size:]

X_train_mean_reversion = X_train_mean_reversion.reshape((X_train_mean_reversion.shape[0], X_train_mean_reversion.shape[1], 1))
X_test_mean_reversion = X_test_mean_reversion.reshape((X_test_mean_reversion.shape[0], X_test_mean_reversion.shape[1], 1))


# Create a function that will use this data in the LSTM process
def create_lstm_model(lookback):
model = Sequential()
model.add(LSTM(units=50, return_sequences=True, input_shape=(lookback, 1)))
model.add(LSTM(units=50, return_sequences=False))
model.add(Dense(units=25))
model.add(Dense(units=1))
model.compile(optimizer='adam', loss='mean_squared_error')
return model


mean_reversion_lstm = create_lstm_model(lookback)

# Train the model
mean_reversion_lstm.fit(X_train_mean_reversion, y_train_mean_reversion, epochs=5, batch_size=32)

# Generate predictions
y_test_mean_reversion = mean_reversion_lstm.predict(X_test_mean_reversion)


# Calculate the z-score of predicted mean reversion values
z_scores = (y_test_mean_reversion - y_test_mean_reversion.mean()) / y_test_mean_reversion.std()

# Define z-score threshold for buy and sell signals
z_threshold = 1

# Generate trading signals
mean_reversion_signals = np.where(z_scores > z_threshold, -1, np.where(z_scores < -z_threshold, 1, 0))

import matplotlib.pyplot as plt

last_train_index = tesla.index[train_size + lookback - 1]

returns = np.diff(tesla.loc[last_train_index + 1:, 'Close'].values) * mean_reversion_signals.flatten()[:-1]

# Calculate cumulative returns
cumulative_returns = np.cumsum(returns)

# Plot cumulative returns
plt.figure(figsize=(14, 6))
plt.plot(tesla.loc[last_train_index + 2:, 'Datetime'].values, cumulative_returns, label='Mean Reversion Strategy Returns')
plt.xlabel('Datetime')
plt.ylabel('Cumulative Returns')
plt.legend()
plt.show()

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

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

Совокупная доходность здесь значительно лучше, и для долгосрочной стратегии это отличный результат для простой модели LSTM. Конечно, это не свидетельствует о будущих доходах, и модель была обучена на исторических данных бла-бла. Но суть вы поняли.

Часть 4.2: Использование искусственных нейронных сетей для улучшения стратегии возврата к среднему в Python

В этой статье будет показано, как использование искусственных нейронных сетей в стратегии возврата к среднему может повысить ее производительность. Кроме того, это будет протестировано на истории, чтобы обеспечить показатель эффективности. Это часть 4ii из 5 из серии «Алгоритмы машинного обучения на рынках». В конце этой серии каждый метод машинного обучения будет сравниваться с их аналогами.

Эта статья будет посвящена прогнозному моделированию; то есть, может ли использование ИНС улучшить прогностическую эффективность стратегии возврата к среднему. Рекуррентные нейронные сети (RNN): RNN предназначены для обработки данных временных рядов, таких как движения цен на акции с течением времени. Они особенно полезны для моделирования последовательностей событий, когда выходные данные одного события возвращаются в сеть в качестве входных данных для следующего события. RNN можно использовать для фиксации закономерностей в ценах на акции, которые сохраняются с течением времени и связаны с возвратом к среднему значению.

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

Во-первых, используется код, использующий RNN сам по себе для прогнозирования данных Tesla. Примечание: Для того, чтобы получить хороший ретроспективный и прогнозный набор, мне пришлось увеличить даты начала / окончания для 15-минутных данных.

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, SimpleRNN, Dropout
from keras.optimizers import Adam

# Download 15-minute Tesla data from yfinance
tesla = yf.Ticker('TSLA')
data = tesla.history(interval='15m', start='2023-04-11', end='2023-04-25')

# Reset the index to make 'Date' a regular column
data = data.reset_index()

# Preprocess the data
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data['Close'].values.reshape(-1, 1))

# Create training and testing datasets
def create_dataset(scaled_data, lookback=60):
X, y = [], []
for i in range(lookback, len(scaled_data)):
X.append(scaled_data[i - lookback:i, 0])
y.append(scaled_data[i, 0])
return np.array(X), np.array(y)

lookback = 60
train_ratio = 0.95
train_size = int(len(scaled_data) * train_ratio)

train_data = scaled_data[:train_size]
test_data = scaled_data[train_size - lookback:]

X_train, y_train = create_dataset(train_data, lookback)
X_test, y_test = create_dataset(test_data, lookback)

X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))

# Build the RNN model
model = Sequential()
model.add(SimpleRNN(50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(Dropout(0.2))
model.add(SimpleRNN(50, return_sequences=True))
model.add(Dropout(0.2))
model.add(SimpleRNN(50))
model.add(Dropout(0.2))
model.add(Dense(1))

model.compile(optimizer=Adam(lr=0.001), loss='mean_squared_error')

# Train the model
model.fit(X_train, y_train, epochs=100, batch_size=64)

# Make predictions
predicted_prices = model.predict(X_test)
predicted_prices = scaler.inverse_transform(predicted_prices)

# Evaluate the model
rmse = np.sqrt(np.mean((predicted_prices - y_test)**2))
print('Root Mean Squared Error:', rmse)

# Plotting the predicted against the actual
num_test_points = len(X_test)
test_datetime_values = data.iloc[-num_test_points:, 0]

# Inverse transform the actual prices (y_test)
actual_prices = scaler.inverse_transform(y_test.reshape(-1, 1))

# Plot actual and predicted stock prices
plt.figure(figsize=(14, 6))
plt.plot(test_datetime_values, actual_prices, label='Actual Prices')
plt.plot(test_datetime_values, predicted_prices, label='Predicted Prices')
plt.xlabel('Datetime')
plt.ylabel('Stock Prices')
plt.title('Tesla Stock Price Prediction')
plt.legend()
plt.show()

Среднеквадратичная ошибка: 160,87779729544997

Теперь, показывая, как это переводится в кумулятивную доходность, показано здесь:

# Calculate the percentage change in predicted prices
predicted_pct_change = np.diff(predicted_prices.reshape(-1)) / predicted_prices[:-1].reshape(-1)

# Get the actual prices corresponding to the predicted_prices in the test set
actual_prices_test = actual_prices[-len(predicted_prices) + 1:]

# Calculate the percentage change in actual prices
actual_pct_change = np.diff(actual_prices_test.reshape(-1)) / actual_prices_test[:-1].reshape(-1)

# Truncate the longer array if the lengths are not equal
if len(predicted_pct_change) != len(actual_pct_change):
min_len = min(len(predicted_pct_change), len(actual_pct_change))
predicted_pct_change = predicted_pct_change[:min_len]
actual_pct_change = actual_pct_change[:min_len]

# Create a trading strategy based on the predicted percentage change
strategy_returns = np.where(predicted_pct_change > 0, 1, -1) * actual_pct_change

# Calculate the cumulative returns
cumulative_returns = (1 + strategy_returns).cumprod()

# Plot the cumulative returns
plt.figure(figsize=(14, 6))
plt.plot(data['Datetime'].iloc[-len(cumulative_returns):], cumulative_returns, label='Strategy Returns')
plt.xlabel('Datetime')
plt.ylabel('Cumulative Returns')
plt.title('Tesla Stock Trading Strategy Returns')
plt.legend()
plt.show()

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

Теперь RNN будет применяться со стратегией возврата к данным Tesla.

import yfinance as yf
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.layers import SimpleRNN

# Download 15-minute Tesla data from yfinance
tesla = yf.download('TSLA', start='2023-04-14', end='2023-04-28', interval='15m')

# Reset the index
tesla.reset_index(inplace=True)

# Preprocess data
data = tesla[['Close']]
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data)


# Create a Function that generates 'good' data for the process
def create_dataset(data, lookback):
features, targets = [], []
for i in range(lookback, len(data)):
features.append(data[i-lookback:i])
targets.append(data[i, 0])
return np.array(features), np.array(targets)

lookback = 60
mean_reversion_features, mean_reversion_targets = create_dataset(scaled_data, lookback)

# Split data into train and test
train_size = int(len(mean_reversion_features) * 0.8)

X_train_mean_reversion, y_train_mean_reversion = mean_reversion_features[:train_size], mean_reversion_targets[:train_size]
X_test_mean_reversion, y_test_mean_reversion = mean_reversion_features[train_size:], mean_reversion_targets[train_size:]

X_train_mean_reversion = X_train_mean_reversion.reshape((X_train_mean_reversion.shape[0], X_train_mean_reversion.shape[1], 1))
X_test_mean_reversion = X_test_mean_reversion.reshape((X_test_mean_reversion.shape[0], X_test_mean_reversion.shape[1], 1))

def create_rnn_model(lookback):
model = Sequential()
model.add(SimpleRNN(units=50, return_sequences=True, input_shape=(lookback, 1)))
model.add(SimpleRNN(units=50, return_sequences=False))
model.add(Dense(units=25))
model.add(Dense(units=1))
model.compile(optimizer='adam', loss='mean_squared_error')
return model

mean_reversion_rnn = create_rnn_model(lookback)

# Train the model
mean_reversion_rnn.fit(X_train_mean_reversion, y_train_mean_reversion, epochs=5, batch_size=32)

# Generate predictions
y_test_mean_reversion = mean_reversion_rnn.predict(X_test_mean_reversion)

# Calculate the z-score of predicted mean reversion values
z_scores = (y_test_mean_reversion - y_test_mean_reversion.mean()) / y_test_mean_reversion.std()

# Define z-score threshold for buy and sell signals
z_threshold = 1

# Generate trading signals
mean_reversion_signals = np.where(z_scores > z_threshold, -1, np.where(z_scores < -z_threshold, 1, 0))

last_train_index = tesla.index[train_size + lookback - 1]

returns = np.diff(tesla.loc[last_train_index + 1:, 'Close'].values) * mean_reversion_signals.flatten()[:-1]

# Calculate cumulative returns
cumulative_returns = np.cumsum(returns)

# Plot cumulative returns
plt.figure(figsize=(14, 6))
plt.plot(tesla.loc[last_train_index + 2:, 'Datetime'].values, cumulative_returns, label='Mean Reversion Strategy Returns')
plt.xlabel('Datetime')
plt.ylabel('Cumulative Returns')
plt.legend()
plt.show()
15-минутный график цены Tesla в разные даты, используемые в этой модели

Приведенные выше графики показывают в первую очередь то, что в течение 15-минутного интервала RNN изначально не дает хорошей/положительной доходности, но, поскольку модель имеет более длительный период прогноза, она улучшается. Модель работает намного лучше, когда используется со стратегией возврата к среднему, чем просто RNN на самих ценовых данных. Глядя на график цены акций, мы видим, что по мере падения цены (примерно в 2023–04–26 годах) растет и доходность, генерируемая этой стратегией. И наоборот, по мере роста цены растет и доходность. Это интересный и немного сбивающий с толку результат. Мы видим, что цена, как правило, падает в диапазоне дат; Мы можем ожидать, что сеть спрогнозирует дальнейшее падение цен и покажет хорошие результаты в этих случаях.

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

Часть 5: Использование алгоритма k-ближайшего соседа для улучшения стратегии возврата к среднему в Python

В этой статье будет показано, как использование модели K-ближайшего соседа в стратегии возврата к среднему может улучшить ее производительность. Кроме того, это будет протестировано на истории, чтобы обеспечить показатель эффективности. Это часть 5 из 5 из серии «Алгоритмы машинного обучения на рынках». В конце этой серии методы машинного обучения будут сопоставлены друг с другом.

Введение
Основная идея, лежащая в основе алгоритма k-NN, заключается в том, что похожие вещи находятся рядом друг с другом. Когда требуется прогноз для невидимой точки данных, алгоритм просматривает ближайшие точки данных «k» или ближайших соседей «k» в обучающем наборе и присваивает значение или класс на основе этих соседей.

Он работает двумя известными методами:

  1. Классификация: Для данной точки данных, которую необходимо классифицировать, алгоритм идентифицирует ближайших соседей «k» из обучающей выборки. Затем точка данных присваивается классу, который имеет наибольшее количество представителей в этих соседях ‘k’. Например, если k = 5, и 3 из 5 ближайших соседей относятся к классу A, а 2 — к классу B, точка данных будет классифицироваться как класс A.
  2. Регрессия: Для задач регрессии алгоритм вычисляет среднее значение (а иногда и медиану) ближайших соседей «k».

Расстояние между точками данных может быть рассчитано различными способами, но наиболее распространенным методом является евклидово расстояние. Другие методы включают расстояние Манхэттена, расстояние Минковского и т. Д.
Ключевым параметром алгоритма k-NN является значение ‘k’. Если ‘k’ слишком мал, модель может быть чрезмерно чувствительна к шуму в данных. Если «k» слишком велика, модель может включать точки, которые находятся слишком далеко и, следовательно, менее релевантны. Выбор правильной буквы «k» часто включает в себя некоторые пробы и ошибки или такие методы, как перекрестная проверка.

Алгоритм k в k-ближайших соседях (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,5192307692307693
Для k = 12 точность = 0,5192307692307693
Для k = 13 точность = 0,5384615384615384
Для k = 14 точность = 0,5961538461538461
Для k = 15 точность = 0,5384615384615384
Для 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=1

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

для k=9
Ценовое действие для данных Tesla с 15-минутными интервалами в диапазоне дат

Для графика 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,5769230769230769
Для 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
Для k = 18 точность = 0,5961538461538461
Для k = 19 точность = 0,5769230769230769

с помощью k = 7
Ценовое действие для данных Tesla с 15-минутными интервалами в диапазоне дат

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

Добавление 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 мы вычисляем точность модели по тестовым данным.

Для 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,46153846153846156
Для k = 8 точность = 0,46153846153846156
Для k = 9 точность = 0,46153846153846156
Для k = 10 точность = 0,46153846153846156
Для k = 11 точность = 0,46153846153846156
Для k = 12 точность = 0,46153846153846156
Для k = 13 точность = 0,46153846153846156
Для k = 14 точность = 0,46153846153846156
Для k = 15 точность = 0,46153846153846156
Для k = 16 точность = 0,46153846153846156
Для k = 17 точность = 0,46153846153846156
Для k = 18 точность = 0,46153846153846156
Для k = 19 точность = 0,46153846153846156

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()
Отдача от стратегии возврата к среднему значению KNN
Ценовое действие данных Tesla с 15-минутными интервалами по всему диапазону дат

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

Дополнительные ограничения

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

Часть 6. Классификация временных рядов с использованием динамического искривления времени K-ближайших соседей

Задумывались ли вы когда-нибудь, есть ли акции, которые движутся по поразительно похожей схеме на ту, за которой вы наблюдаете? Что ж, мы углубились в этот вопрос и нашли увлекательный подход к разгадке этой тайны!

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

Но это только половина головоломки. Мы связываем DTW с алгоритмом K-ближайших соседей (KNN). KNN — это простой, но мощный алгоритм, который сканирует «самые близкие» элементы к интересующему вас элементу. В нашем случае он охотится за акциями, которые имеют наиболее схожие ценовые модели с выбранными нами акциями.

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

В этой статье мы раскроем волшебство методов K-ближайших соседей (KNN) и динамического искривления времени (DTW) и рассмотрим, как их можно использовать для классификации данных временных рядов. С помощью KNN и DTW мы можем идентифицировать акции, которые демонстрируют схожие тенденции с конкретной интересующей нас акцией. Мы надеемся вооружить вас этими инструментами для выявления потенциальных корреляций и, возможно, даже прогнозирования будущих движений акций. Давайте отправимся в это интригующее путешествие вместе!

Что такое анализ временных рядов?

Временной ряд — это ряд точек данных, индексированных во временном порядке. Чаще всего временной ряд представляет собой последовательность, снятую в последовательные равноудаленные моменты времени. Таким образом, это последовательность данных с дискретным временем. Временной ряд — это часто строящаяся диаграмма. Он отлично применяется в реальном мире, включая статистику, обработку сигналов, распознавание образов, прогнозирование, здравоохранение, астрономию и практически любую область науки и техники, связанную с измерением временных данных.

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

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

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

DTW: Что, почему, как?

Что такое DTW?

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

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

По своей сути DTW является инструментом анализа временных рядов. Что делает его особенным? В то время как традиционные методы могут испытывать трудности, когда последовательности танцуют под разные доли (или не синхронизированы или имеют разную длину), DTW элегантно «искажает» их временные шкалы для оптимального выравнивания. Он понимает, что две последовательности могут иметь одну и ту же сущность, даже если они имеют разные скорости или тайминги. Таким образом, в отличие от классического метода евклидовой дистанции, который может не одобрить наших танцоров с уникальным темпом, DTW прославляет их индивидуальность и при этом находит гармонию.

Проще говоря, думайте о DTW как о матчмейкере, который гарантирует, что две различные последовательности временных рядов, независимо от того, насколько они разные, синхронизируют свой идеальный момент!

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

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

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

Итак, сравнивая данные временных рядов, хотите ли вы быть строгим хронометристом или энтузиастом танцев, который ценит ритм и поток? DTW упрощает выбор!

Мы будем использовать Python для визуализации и реализации модели DTWfrom dtaidistance import dtw
from dtaidistance import dtw_visualisation as dtwvis
import numpy as np
x = np.arange(0, 20, .5)
s1 = np.sin(x)
s2 = np.sin(x — 1)
path = dtw.warping_path(s1, s2)
dtwvis.plot_warping(s1, s2, path)
distance = dtw.distance(s1, s2)

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

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

Хотите знать, как мы заполняем эту систему показателей или, другими словами, вычисляем записи матрицы затрат? Что ж, давайте раскроем этот секрет дальше!

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

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

Но есть одна загвоздка. Каким бы очаровательным ни был этот танец, DTW имеет слишком медленный ритм, особенно когда дело доходит до сложности времени. Представьте себе, что вы хотите соединить этот танец с другим, например, KNN; Может показаться, что в ожидании грандиозного спектакля целую вечность. Итак, нам нужен умный хореограф, что является стратегией оптимизации, чтобы ускорить процесс, не теряя при этом сути танца.d, paths = dtw.warping_paths(s1, s2, window=20, use_pruning=True )
best_path = dtw.best_path(paths)
dtwvis.plot_warpingpaths(s1, s2, paths, best_path)

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

Именно так мы и поступили здесь. Используя window=20, мы устанавливаем границу, гарантируя, что только точки из s1 и s2 в определенном диапазоне могут быть выровнены друг относительно друга. Думайте об этом как о сужении танцевальной сцены в зависимости от того, где на самом деле происходит действие.

И это еще не все! С use_pruning=True мы добавляем мастер-ход. Это похоже на проницательного режиссера, который быстро замечает и удаляет любые пути, которые не добавляют к основному спектаклю. С какой целью? Чтобы танец — или, в нашем случае, алгоритм — оставался сфокусированным на оптимальном пути, делая наш вычислительный танец не только красивым, но и эффективным.

KNN: Что, почему, как?

Что такое алгоритм KNN?

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

Примерно так работает алгоритм KNN (k-nearest neighbours). По своей сути KNN похож на дружелюбного тусовщика, классифицирующего неизвестные точки данных, сопоставляя их с наиболее похожими аналогами из известного набора данных.

Вот простой способ визуализировать это:

Когда k равно 1: Думайте об этом, как о том, что вы спрашиваете мнение только одного человека на вечеринке. В терминах KNN тестовый пример будет принимать метку своего ближайшего соседа из обучающего набора.

Когда k равно 3: это все равно, что искать мнения трех человек и следовать мнению большинства. Таким образом, если два из трех человек дают один и тот же совет, вы следуете этому совету. Алгоритм KNN просмотрит метки трех ближайших точек данных и выберет наиболее распространенную метку.

В чем сложность? Выбор правильной буквы «к». Точно так же, как вам нужно решить, со сколькими людьми проконсультироваться на вечеринке, в KNN выбор правильного значения для «k» имеет решающее значение. Речь идет не о том, чтобы спросить больше людей, а о том, чтобы спросить нужное количество людей. Для определения этого магического числа пригодятся такие методы, как перекрестная проверка, когда мы проверяем точность алгоритма с помощью различных значений ‘k’ на меньших блоках данных.

По сути, KNN — это поиск своей аудитории в море данных и принятие решений, основанных на коллективной мудрости!

Как работает алгоритм KNN?

  • Шаг 1: Выберите количество k соседей
  • Шаг 2: Вычислите евклидово расстояние k соседей
  • Шаг 3: Возьмите k ближайших соседей в соответствии с вычисленным евклидовым расстоянием.
  • Шаг 4: Среди этих k соседей подсчитайте количество точек данных в каждой категории.
  • Шаг 5: Отнесите новые точки данных к той категории, для которой количество соседей максимально.
  • Шаг 6: Наша модель готова.

Зачем нужен KNN?

Представьте, что вы находитесь на грандиозной вечеринке с двумя эксклюзивными группами: командой А и командой Б. Вы замечаете кого-то нового, назовем его Алекс, входящего в комнату. Животрепещущий вопрос заключается в том, к какой группе присоединится Алекс?

На сцену выходит алгоритм K-NN (K-Nearest Neighbors), наш социальный детектив на вечер. С помощью K-NN мы можем быстро определить, является ли Алекс больше человеком команды А или команды Б, основываясь на определенных характеристиках или сходстве с членами каждой группы. Итак, в следующий раз, когда вы увидите новичка и зададитесь вопросом, где он лучше всего подойдет, помните, что у K-NN может быть ответ!

Как правильно выбрать значение К?

Представьте себе, что вы настраиваете волшебный телескоп, который поможет вам заглянуть в закономерности Вселенной. Но вот в чем загвоздка — вы должны выбрать идеальный объектив, чтобы получить максимально четкое и точное изображение.

Выбор правильной буквы «k» в K-NN немного похож на поиск идеального объектива. Вы же не хотите выбрать случайный и получить размытое зрение. Итак, что же делать? Вы тестируете различные объективы (или значения ‘k’) и выбираете тот, который дает наиболее четкую картинку с наименьшими искажениями.

В нашей галактике данных это означает выбор буквы «k», которая минимизирует ошибки, но сохраняет предсказательную силу нашего алгоритма. Это баланс, но, проявив немного терпения и тестирования, вы найдете золотую середину и раскроете секреты своих данных!

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

Если присмотреться, то можно заметить нечто завораживающее. Если мы установим наш надежный алгоритм K-NN в K=3, он, по-видимому, склоняется к тому, чтобы поместить нашу таинственную точку данных в класс B. Но немного закрутите сюжет и поднимите K до 7, и внезапно наша точка зрения кажется более близкой к классу А. Запутались? Не стоит.

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

  • Начните с простого: начните со случайной буквы «k». Это начало, и каждая эпическая история нуждается в нем!
  • Маленькое k = Wild Ride: Крошечное значение ‘k’ может дать вам границы принятия решений, которые будут похожи на американские горки — захватывающие, но нестабильные.
  • Большое k = плавное плавание: И наоборот, большая буква «k» смягчит эти резкие колебания и даст более стабильную (и часто более точную) классификацию.
  • График: Визуализируйте взаимосвязь между значениями ‘k’ и частотой ошибок. Как правило, вы хотите найти золотую середину, где частота ошибок снижается до самого низкого уровня.
  • Так что в следующий раз, когда вы будете бороться с буквой «к», помните: дело не только в числах. Речь идет о понимании данных, экспериментировании и визуализации. Погружайтесь, настраивайтесь и позвольте данным направлять вас.

Теперь давайте объединим магию DTW (динамическое искривление времени) с интуитивным мастерством K-NN (k-Nearest Neighbors), чтобы создать модель, предназначенную для прогнозирования биржевых трендов. Цель? Чтобы привлечь внимание к акциям, которые танцуют в том же ритме, что и выбранные нами акции.

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

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

МОДЕЛЬ

Представьте себе каждую акцию на рынке как танцора со своим уникальным ритмом и движениями. Некоторые танцоры имеют пугающе похожие паттерны, даже если иногда они танцуют немного не в ногу. Представляем наш динамический дуэт: динамическое искривление времени (DTW) и алгоритм K-ближайших соседей (KNN). Вместе они похожи на наших судей по танцам, замечая пары и группы с похожими ритмами и вибрациями, даже если они иногда пропускают ритм.

Хотите узнать, как научить наш компьютер быть идеальным судьей? Давайте сразу перейдем к технической части.

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

Эта версия рисует яркую картину, используя аналогию с танцем, и непринужденно переходит к техническим деталям.

Ладно, давайте засучим рукава и окунемся в магию программирования! Но прежде чем мы придадим какие-либо алгоритмы, нам нужно немного подготовиться. Думайте об этом, как о подготовке ингредиентов перед тем, как мы начнем готовить. Вот изящная команда, чтобы собрать все цифровые специи, которые нам понадобятся:pip install yfinance fastdtw numpy scikit-learn

И вот настал момент, которого вы все так долго ждали: давайте представим код!import yfinance as yf
import numpy as np
from fastdtw import fastdtw
from sklearn.model_selection import KFold, s
from sklearn.neighbors import KNeighborsClassifier

# Computes the Dynamic Time Warping (DTW) distance between two stock time series.
# The lower the result, the more similar the two stock patterns are.
def dtw_distance(stock_1, stock_2):
distance, _ = fastdtw(stock_1, stock_2)
return float(distance)

# To download Stock Data
# We’ll use yfinance to get the closing prices of various companies for a specified time period.
tickers = [«AAPL», «MSFT», «AMZN», «META», «GOOGL», «GOOG», «TSLA», «JPM», «JNJ», «V», «PG», «NVDA», «HD», «UNH», «PYPL»]
start_date = «2022–01–01»
end_date = «2023–01–01»
data = {ticker: yf.download(ticker, start=start_date, end=end_date)[‘Close’].tolist() for ticker in tickers}

# Prepare data for our model
X = [data[ticker] for ticker in tickers]
y = tickers

# Calculate Similarities Between Stocks
# We’re going to compare every stock’s price patterns with every other stock’s patterns.
dtw_matrix = np.zeros((len(tickers), len(tickers)))
for i in range(len(tickers)):
for j in range(len(tickers)):
dtw_matrix[i, j] = dtw_distance(X[i], X[j])

print(«\nHow Similar Are These Stocks?»)
print(dtw_matrix)

# Find Most Similar Stocks
# Using K-nearest neighbours with our DTW distance metric to find the most similar stocks.
knn = KNeighborsClassifier(metric=dtw_distance)

# Using KFold to split our data into parts (or «folds») for cross-validation.
cv = KFold(n_splits=3, shuffle=True, random_state=42)

# Let’s try different values for K (number of neighbors) to find the best one.
param_grid = {‘n_neighbors’: list(range(1, min(len(tickers)-1, 10)))}

# GridSearchCV will try out each value of K and tell us the best one.
grid_search = GridSearchCV(knn, param_grid, cv=cv)
grid_search.fit(X, y)

print(f»\nBest Number of Neighbors: {grid_search.best_params_[‘n_neighbors’]}»)

# For each stock, let’s find out which other stock is most similar to it.
distances, neighbors_idx = grid_search.best_estimator_.kneighbors(X, 2)
for i, ticker in enumerate(tickers):
print(f»\n{ticker}’s closest buddy is {tickers[neighbors_idx[i][1]]} with a similarity score of {distances[i][1]}.»)

РЕЗУЛЬТАТЫ

Хорошо, давайте разберем, что только что сделал наш код:

Представьте себе каждую акцию как человека в огромной компании. Теперь наш код играл роль свахи и находил лучшего приятеля (или «ближайшего соседа») для каждого гостя акции. И знаете что? И это еще не все. Он также пролил свет на то, насколько близки эти друзья по складу на самом деле, показав нам расстояние DTW между ними. Думайте об этом, как о том, насколько синхронны их танцевальные движения!

И знаете, что еще круче? Наш код не просто случайно выбрал напарника. Он приложил дополнительные усилия и вычислил идеальное число (или «оптимальное k») для размера группы, чтобы обеспечить наилучшие матчи. Это похоже на то, как если бы все попали в танцевальную группу идеального размера, где их движения идеально совпадают.

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

Эта версия использует беззаботный, понятный подход, сравнивая акции с тусовщиками и используя аналогию с танцем, чтобы объяснить концепцию ближайших соседей и расстояния DTW.Best Number of Neighbors: 1

AAPL’s closest buddy is PG with a similarity score of 1692.610008239746.

MSFT’s closest buddy is TSLA with a similarity score of 5961.919708251953.

AMZN’s closest buddy is GOOG with a similarity score of 1904.8935852050781.

META’s closest buddy is NVDA with a similarity score of 4677.560478210449.

GOOGL’s closest buddy is GOOG with a similarity score of 114.77847290039062.

GOOG’s closest buddy is GOOGL with a similarity score of 114.77847290039062.

TSLA’s closest buddy is NVDA with a similarity score of 5553.903793334961.

JPM’s closest buddy is PG with a similarity score of 1971.1000518798828.

JNJ’s closest buddy is AAPL with a similarity score of 3678.819969177246.

V’s closest buddy is NVDA with a similarity score of 8016.499649047852.

PG’s closest buddy is AAPL with a similarity score of 1670.3999862670898.

NVDA’s closest buddy is META with a similarity score of 4684.2404708862305.

HD’s closest buddy is MSFT with a similarity score of 6944.269836425781.

UNH’s closest buddy is HD with a similarity score of 50169.78009033203.

PYPL’s closest buddy is GOOGL with a similarity score of 2227.4922943115234.

ЗАКЛЮЧЕНИЕ

Хорошо, давайте немного поговорим о данных временных рядов. Это может быть настоящим головокружением, не так ли? Со всеми его последовательными паттернами и этими надоедливыми временными сдвигами, его анализ не всегда является прогулкой по парку. Но знаете что? Как мы уже говорили, есть классная комбинация, которая может прийти нам на помощь: Динамическое искривление времени (DTW) и k-Ближайшие соседи (KNN).

Вот в чем суть: DTW похож на того гениального друга, который может заметить сходство между двумя песнями, даже если одна из них немного быстрее. Он выравнивает данные временных рядов, гарантируя, что мы сравниваем яблоки с яблоками, даже если одно яблоко созрело немного раньше. Теперь добавьте к этому KNN, удобный инструмент, который выявляет закономерности на основе того, что распространено в «районе». У вас есть звездная команда, которая займется классификацией временных рядов.

Независимо от того, пытаетесь ли вы контролировать движения фондового рынка, замечать что-то необычное в показаниях датчиков машин или просто обрабатывать любые данные, привязанные ко времени, эту пару определенно стоит добавить в свой набор инструментов для работы с данными. Но помните, ни один инструмент не является волшебным! Вы должны настроить его правильно и по-настоящему почувствовать свои данные. Но как только вы это сделаете, какие идеи вы сможете почерпнуть с помощью DTW и KNN? Чистое золото.

ССЫЛКИ

Временные ряды —

https://medium.com/@varun030403/time-series-forecasting-theoretical-aspects-part-1-9adf7b1e0ce3

Динамическое искривление времени —

Динамическое искривление времени (DTW)

https://rtavenar.github.io/blog/dtw.html

KNN —

https://medium.com/analytics-vidhya/k-nearest-neighbors-knn-8f027ae1228f

Источник

Источник