В этой статье вы узнаете, как работает неконтролируемое машинное обучение и как находить пары, используя биржевые данные для парной торговли.
Здесь мы будем использовать три типа методов кластерного анализа, т. е.
i) Кластеризация K-средних.
ii) Иерархический кластер
iii) Кластеризация распространения сходства
Загрузите необходимый набор данных:
СКАЧАТЬ НАБОР ДАННЫХ ЗДЕСЬ
Этот набор данных содержит список 1000 лучших компаний на основе рыночной капитализации индийских компаний на март 2020 года. Нам нужен этот набор данных
Индийские биржевые данные неудобны. Поэтому необходимо иметь список компаний и перебирать их для получения данных.
- Импортировать необходимые модули:
from nsepy import get_history from datetime import date import pandas as pd import numpy as np import seaborn as sns from sklearn.preprocessing import StandardScaler
2. Чтение файла excel для получения списка компаний:
df = pd.read_excel("MCAP_31032020_TOP1000.xlsx") stock_1000 = list(df.Symbol)
3. Получение списка 500 лучших компаний:
stock = stock_1000[:500]
4. Проходим по списку и получаем наши данные по каждой из компаний:
the_stock_data = {} for symbols in stock: try: the_stock_data[symbols] = get_history(symbol = symbols , start = date(2019, 1, 1), end = date(2022, 1,31)) except: continue
Здесь мы указываем дату начала и дату окончания.
5. Объединение полученных данных:
data = pd.concat(the_stock_data)
6. Сброс индекса:
data = data.reset_index()
7. Извлечение необходимых данных, т. е. даты и цены закрытия.
data = data.pivot(index='Date', columns='Symbol', values = 'Close') data.head()
8. Описываем метод и ставим десятичную точку на 3:
pd.set_option('precision', 3) data.describe().T.head(10)
9. Обработка нулевых значений:
Проверка существования нулевых значений:
data.isnull().values.any()
Визуализация нулевых значений с использованием отсутствующих:
import missingno missingno.matrix(data)
Удаление столбцов с нулевыми значениями более 20% данных:
print('Data Shape before cleaning =', data.shape) missing_percentage = data.isnull().mean().sort_values(ascending=False) dropped_list = sorted(list(missing_percentage[missing_percentage > 0.2].index)) data.drop(labels=dropped_list, axis=1, inplace=True) print('Data Shape after cleaning =', data.shape)
Заполнение нулевых значений с использованием методов ffill и bfill:
data = data.fillna(method='ffill') data = data.fillna(method='bfill')
Теперь наш набор данных не содержит нулевых значений.
12. Хранение данных для будущего использования:
data.to_csv('NSE500_stock_data')
13. Расчет доходности и волатильности и создание фрейма данных:
#Calculate returns and create a data frame returns = data.pct_change().mean()*266 returns = pd.DataFrame(returns) returns.columns = ['returns'] #Calculate the volatility returns['volatility'] = data.pct_change().std()*np.sqrt(266) data = returns data.head()
14. Визуализация данных (доходность и волатильность):
sns.displot(data, x="returns", y="volatility")
15. Использование StandardScalar для преобразования данных:
#Prepare the scaler scale = StandardScaler().fit(data) #Fit the scaler scaled_data = pd.DataFrame(scale.fit_transform(data),columns = data.columns, index = data.index) X = scaled_data X.head()
16. Визуализация данных после использования Standard Scalar:
sns.displot(X, x="returns", y="volatility")
17. Выполнение кластеризации K-средних:
Нахождение количества кластеров
i) Метод локтя:
from sklearn.cluster import KMeans from sklearn import metrics import matplotlib.pyplot as plt %matplotlib inline K = range(1,15) distortions = [] #Fit the method for k in K: kmeans = KMeans(n_clusters = k) kmeans.fit(X) distortions.append(kmeans.inertia_) #Plot the results fig = plt.figure(figsize= (15,5)) plt.plot(K, distortions, 'bx-') plt.xlabel('Values of K') plt.ylabel('Distortion') plt.title('Elbow Method') plt.grid(True) plt.show()
Использование библиотеки Kned, которая находит оптимальное количество кластеров:
from kneed import KneeLocator kl = KneeLocator(K, distortions, curve="convex", direction="decreasing") kl.elbow
ii) Метод силуэта:
from sklearn.metrics import silhouette_score #For the silhouette method k needs to start from 2 K = range(2,15) silhouettes = [] #Fit the method for k in K: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10, init='random') kmeans.fit(X) silhouettes.append(silhouette_score(X, kmeans.labels_)) #Plot the results fig = plt.figure(figsize= (15,5)) plt.plot(K, silhouettes, 'bx-') plt.xlabel('Values of K') plt.ylabel('Silhouette score') plt.title('Silhouette Method') plt.grid(True) plt.show()
Использование библиотеки Kned, которая находит оптимальное количество кластеров:
kl = KneeLocator(K, silhouettes, curve="convex", direction="decreasing") print('Suggested number of clusters: ', kl.elbow)
Построение нашего алгоритма k-Means с 4 кластерами:
c = 4 #Fit the model k_means = KMeans(n_clusters=c) k_means.fit(X) prediction = k_means.predict(X) #Plot the results centroids = k_means.cluster_centers_ fig = plt.figure(figsize = (18,10)) ax = fig.add_subplot(111) scatter = ax.scatter(X.iloc[:,0],X.iloc[:,1], c=k_means.labels_, cmap="rainbow", label = X.index) ax.set_title('k-Means Cluster Analysis Results') ax.set_xlabel('Mean Return') ax.set_ylabel('Volatility') plt.colorbar(scatter) plt.plot(centroids[:,0],centroids[:,1],'sg',markersize=10) plt.show()
Нахождение количества экземпляров в каждом кластере:
clustered_series = pd.Series(index=X.index, data=k_means.labels_.flatten()) clustered_series_all = pd.Series(index=X.index, data=k_means.labels_.flatten()) clustered_series = clustered_series[clustered_series != -1] plt.figure(figsize=(12,8)) plt.barh(range(len(clustered_series.value_counts())),clustered_series.value_counts()) plt.title('Clusters') plt.xlabel('Stocks per Cluster') plt.ylabel('Cluster Number') plt.show()
18. Выполнение иерархической кластеризации:
#x-axis - stock, y-axis - distance between them from sklearn.cluster import AgglomerativeClustering import scipy.cluster.hierarchy as shc plt.figure(figsize=(15, 10)) plt.title("Dendrograms") dend = shc.dendrogram(shc.linkage(X, method='ward'))
Находим количество кластеров:
plt.figure(figsize=(15, 10)) plt.title("Dendrogram") dend = shc.dendrogram(shc.linkage(X, method='ward')) plt.axhline(y=9.5, color='purple', linestyle='--')
Построение нашего алгоритма иерархической кластеризации с 5 кластерами:
#Fit the model clusters = 5 hc = AgglomerativeClustering(n_clusters= clusters, affinity='euclidean', linkage='ward') labels = hc.fit_predict(X) #Plot the results fig = plt.figure(figsize=(15,10)) ax = fig.add_subplot(111) scatter = ax.scatter(X.iloc[:,0], X.iloc[:,1], c=labels, cmap='rainbow') ax.set_title('Hierarchical Clustering Results') ax.set_xlabel('Mean Return') ax.set_ylabel('Volatility') plt.colorbar(scatter) plt.show()
19. Выполнение кластеризации распространения сходства:
Построение нашего алгоритма кластеризации Affinity Propagation:
from sklearn.cluster import AffinityPropagation #Fit the model ap = AffinityPropagation() ap.fit(X) labels1 = ap.predict(X) #Plot the results fig = plt.figure(figsize=(15,10)) ax = fig.add_subplot(111) scatter = ax.scatter(X.iloc[:,0], X.iloc[:,1], c=labels1, cmap='rainbow') ax.set_title('Affinity Propagation Clustering Results') ax.set_xlabel('Mean Return') ax.set_ylabel('Volatility') plt.colorbar(scatter) plt.show()
Получение количества кластеров и их расположение для лучшего вида:
from itertools import cycle #Extract the cluster centers and labels cci = ap.cluster_centers_indices_ labels2 = ap.labels_ #Print their number clusters = len(cci) print('The number of clusters is:',clusters) #Plot the results X_ap = np.asarray(X) plt.close('all') plt.figure(1) plt.clf fig=plt.figure(figsize=(15,10)) colors = cycle('cmykrgbcmykrgbcmykrgbcmykrgb') for k, col in zip(range(clusters),colors): cluster_members = labels2 == k cluster_center = X_ap[cci[k]] plt.plot(X_ap[cluster_members, 0], X_ap[cluster_members, 1], col + '.') plt.plot(cluster_center[0], cluster_center[1], 'o', markerfacecolor=col, markeredgecolor='k', markersize=12) for x in X_ap[cluster_members]: plt.plot([cluster_center[0], x[0]], [cluster_center[1], x[1]], col) plt.show()
20. Использование оценки силуэта, чтобы определить, какой метод работает лучше всего:
print("k-Means Clustering", metrics.silhouette_score(X, k_means.labels_, metric='euclidean')) print("Hierarchical Clustering", metrics.silhouette_score(X, hc.fit_predict(X), metric='euclidean')) print("Affinity Propagation Clustering", metrics.silhouette_score(X, ap.labels_, metric='euclidean'))
Здесь алгоритм K-средних показал себя хорошо.
21. Извлекать торговые пары
i) Общее количество кластеров и общее количество пар находится:
cluster_size_limit = 1000 counts = clustered_series.value_counts() symbol_count = counts[(counts>1) & (counts<=cluster_size_limit)] print ("Number of clusters: %d" % len(symbol_count)) print ("Number of Pairs: %d" % (symbol_count*(symbol_count-1)).sum())
ii) Чтение данных, которые мы сохранили ранее:
data1 = pd.read_csv("NSE500_stock_data")
ii) Поиск уникальных пар:
def find_cointegrated_pairs(data, significance=0.05): n = data.shape[1] score_matrix = np.zeros((n, n)) pvalue_matrix = np.ones((n, n)) keys = data.keys() pairs = [] for i in range(1): for j in range(i+1, n): S1 = data[keys[i]] S2 = data[keys[j]] result = coint(S1, S2) score = result[0] pvalue = result[1] score_matrix[i, j] = score pvalue_matrix[i, j] = pvalue if pvalue < significance: pairs.append((keys[i], keys[j])) return score_matrix, pvalue_matrix, pairs from statsmodels.tsa.stattools import coint cluster_dict = {} for i, clust in enumerate(symbol_count.index): symbols = clustered_series[clustered_series == clust].index score_matrix, pvalue_matrix, pairs = find_cointegrated_pairs(data1[symbols]) cluster_dict[clust] = {} cluster_dict[clust]['score_matrix'] = score_matrix cluster_dict[clust]['pvalue_matrix'] = pvalue_matrix cluster_dict[clust]['pairs'] = pairs pairs = [] for cluster in cluster_dict.keys(): pairs.extend(cluster_dict[cluster]['pairs']) print ("Number of pairs:", len(pairs)) print ("In those pairs, we found %d unique symbols." % len(np.unique(pairs))) print(pairs)
22. Визуализируйте торговые пары с помощью TSNE (встраивание стохастического соседа с t-распределением):
from sklearn.manifold import TSNE import matplotlib.cm as cm stocks_data = np.unique(pairs) X_data = pd.DataFrame(index=X.index, data=X).T in_pairs_series = clustered_series.loc[stocks_data] stocks = list(np.unique(pairs)) X_pairs = X_data.T.loc[stocks] X_pairs.head() X_tsne = TSNE(learning_rate=30, perplexity=5, random_state=42, n_jobs=-1).fit_transform(X_pairs) X_tsne plt.figure(1, facecolor='white',figsize=(15,10)) plt.clf() plt.axis('off') for pair in pairs: ticker1 = pair[0] loc1 = X_pairs.index.get_loc(pair[0]) x1, y1 = X_tsne[loc1, :] ticker2 = pair[0] loc2 = X_pairs.index.get_loc(pair[1]) x2, y2 = X_tsne[loc2, :] plt.plot([x1, x2], [y1, y2], 'k-', alpha=0.3, c='b'); plt.scatter(X_tsne[:, 0], X_tsne[:, 1], s=215, alpha=0.8, c=in_pairs_series.values, cmap=cm.Paired) plt.title('TSNE Visualization of Pairs'); # Join pairs by x and y for x,y,name in zip(X_tsne[:,0],X_tsne[:,1],X_pairs.index): label = name plt.annotate(label, (x,y), textcoords="offset points", xytext=(0,10), ha='center') plt.show()
Результат визуализации:
Надеюсь, вам понравилось изучение неконтролируемого машинного обучения!
Продолжайте изучать!