Bonjour. C'est la première fois que je publie un article décent sur Qiita. Récemment, j'ai commencé à étudier l'apprentissage automatique. Il va sans dire que l'apprentissage automatique est utilisé dans de nombreux endroits. Filtrage des spams, recommandations de produits, etc ... Les exemples sont infinis. Même ainsi, je souhaitais prédire les cours des actions et les devises à l'aide de l'apprentissage automatique, donc aujourd'hui Je voudrais prédire FX en utilisant l'arbre __decision, qui est l'un de l'apprentissage automatique. Si vous pouvez prédire les cours des actions et les devises avec une bonne précision, vous pouvez gagner de l'argent sans rien faire, c'est donc une histoire très rêveuse. Cependant, en réalité, ce n'est pas si doux que cela puisse être facilement prédit, donc la motivation principale est d'appliquer l'apprentissage automatique que j'ai récemment étudié à quelque chose plutôt que de gagner ou de faire un profit. Alors tout d'abord, si vous lisez cet article __ "Je me fiche de l'apprentissage automatique, alors prévoyez les effets de change avec l'IA et dites-moi si le yen dollar de demain augmentera ou diminuera!" Il n'y a aucune information utile pour ceux qui disent. Pour ceux qui s'intéressent à l'apprentissage automatique et à la prédiction FX, cela peut être un peu amusant. C'est à peu près ça.
Dans le cas des actions, nous savons tous que si vous achetez des actions et que le cours de l'action augmente, vous ferez un profit, et si le cours de l'action baisse, vous perdrez. Certaines personnes ne connaissent peut-être pas FX, donc je vais vous l'expliquer pour le moment. Par exemple, disons que vous négociez pour 100 yens par dollar. Disons que vous passez une commande «d'achat» de 1 $ dans cet état. Demain ce dollar
C'est vrai. Ce type de transaction d'échange s'appelle FX. Dans le cas du FX, vous pouvez appliquer un effet de levier, donc si vous multipliez l'effet de levier par 10, vous pouvez déplacer 10 fois plus d'argent. Dans ce cas, le profit est de 10 fois et la perte de 10 fois, alors soyez prudent. Dans le cas des transactions dollar-yen, on parle de dollar-yen. Si la valeur du dollar augmente comme 100 yens pour un dollar -> 110 yens, le dollar se raffermira (yen faible), et inversement si la valeur du dollar chute comme 100 yens pour un dollar -> 90 yens, le dollar s'affaiblira (yen fort). Ce sera. Je n'expliquerai plus le FX car il y a tellement de livres et de sites qui expliquent le FX d'une manière facile à comprendre, mais le fait est que vous pouvez faire un profit si vous pouvez prédire s'il augmentera ou baissera comme les actions __ à propos de ça.
J'ai fait référence à ici. Une explication détaillée de l'arbre de décision est également publiée ici, je ne l'expliquerai donc pas en détail dans cet article. En un mot, l'arbre de décision ne nécessite pas le travail de mise à l'échelle des quantités de caractéristiques appelées standardisation, et il est facile d'interpréter le processus dans lequel le résultat a été obtenu (c'est-à-dire l'interprétabilité). il y a. Voici un bref résumé de ce qui se passe sur la page liée.
C'est comme ça. La précision de la prédiction n'est pas si élevée car le but principal est de présenter comment appliquer l'apprentissage automatique (arbre de décision) au FX plutôt que de prévoir sérieusement.
C'est un endroit comme ça. En ce qui concerne l'augmentation des caractéristiques, nous avons appris des centaines de jours de données sur la page ci-dessus, mais nous utilisons le "prix ouvert", le "prix de clôture", le "prix élevé" et le "prix bas" comme caractéristiques lors de la prévision. Il n'y en a que quatre. En d'autres termes, au moment de décider si le yen dollar de demain augmentera ou baissera, il n'est décidé que par le chandelier du «jour». Cependant, lorsque les traders prennent réellement des décisions, ils utilisent souvent divers indicateurs techniques tels que les moyennes mobiles (valeurs moyennes des n derniers jours), les bandes de Bollinger et les MACD. Donc, cette fois, en plus des quatre qualités ci-dessus, __ "Moyenne et variance des cours de clôture sur 5, 25, 50 et 75 jours" , __ "Prix d'ouverture, de clôture et prix élevés sur les 3 derniers jours" , Prix bas " etc. Je voudrais ajouter du nouveau. Ces valeurs sont liées aux indicateurs techniques mentionnés précédemment. Vous ne savez pas de quoi vous parlez? Lors de la prédiction du yen dollar de demain, il est préférable de se référer non seulement aux mouvements de prix d'aujourd'hui, mais aussi aux valeurs moyennes des derniers jours à quelques semaines et à combien elles ont fluctué, n'est-ce pas? C'est. Pour le mettre dans une analogie très déroutante "Le riz d'aujourd'hui était du riz au curry préparé par ma mère. D'après les tendances passées, le riz du lendemain après le riz au curry sera probablement un hamburger, alors demain sera un hamburger!" Est la méthode que nous faisons ci-dessus. Ce que je veux faire "Le riz d'aujourd'hui était du curry et du riz. Hier c'était de la viande, mais la semaine dernière, il y avait beaucoup de nourriture chinoise. Je suppose que d'après le menu d'aujourd'hui et les tendances des dernières semaines ... demain, c'est le hamburger!" Je me sens comme. C'est difficile à comprendre.
Tout comme la page ci-dessus, jupyter utilise Python pour gribouiller. La page est presque la même que la page présentée ci-dessus, mais je vais le faire dans l'ordre. Tout d'abord, importez diverses bibliothèques requises.
import pandas as pd
import numpy as np
#Bibliothèque de visualisation de données
import matplotlib.pyplot as plt
#Bibliothèque d'apprentissage automatique
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
#importer graphviz
import graphviz
#Pour la recherche de grille et la validation croisée
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV
Les deux derniers sont nécessaires lors de la recherche de grille et de la vérification des intersections. Chargez ensuite les données. Cette fois, nous avons préparé des données csv pour 2 ans de 2017 à 2019. Je négocie avec un logiciel appelé MT4, et j'y ai apporté les données fournies au format csv.
#Lisez le fichier CSV. 2017-2 ans de 2019
df = pd.read_csv('usd_jpy_api_2017_2019.csv')
#Vérifiez les 5 dernières lignes
df.tail()
Les 5 premières lignes ressemblent à ceci. Il représente le temps, le prix de clôture, le prix d'ouverture, le prix élevé, le prix bas et le volume de négociation de chaque transaction.
Étant donné que le travail jusqu'à présent est la page présentée ci-dessus, je vais l'ignorer grossièrement, mais je donnerai l'étiquette de réponse correcte avec 0, 1 selon que le cours de clôture du dernier jour augmentera.
#Cours de clôture le jour suivant-Calculez la différence au cours de clôture du jour
#shift(-1)Rapprochez-vous d'un
df['close+1'] = df.close.shift(-1)
df['diff'] = df['close+1'] - df['close']
#Fermer le dernier jour+1 devient NaN, alors coupez-le
df = df[:-1]
Vérifions le rapport entre les données en hausse et en baisse.
#Vérifiez le rapport de données de montée et de descente
m = len(df['close'])
#df['diff']>0 renvoie vrai ou faux pour toutes les lignes. df[(df['diff'] > 0)]Avec dff>Sortie de toutes les colonnes en réduisant à 0
print(len(df[(df['diff'] > 0)]) / m * 100)
print(len(df[(df['diff'] < 0)]) / m * 100)
52.16284987277354
47.837150127226465
Il y a plusieurs jours où il a légèrement augmenté. Supprimez ensuite les colonnes indésirables et nommez la cible de l'étiquette.
df.rename(columns={"diff" : "target"}, inplace=True)
#Supprimer les colonnes inutiles
del df['close+1']
del df['time']
#Tri des colonnes
df = df[['target', 'volume', 'open', 'high', 'low', 'close']]
#Sortez les 5 premières lignes
df.head()
À partir de là, nous calculerons les nouvelles fonctionnalités et les ajouterons.
#Calcul de la moyenne mobile, 5 jours, 25 jours, 50 jours, 75 jours
#Calculez également std. (=A les mêmes informations que le groupe de Bollinger)
#Données sécurisées pendant 75 jours
for i in range(1, 75):
df['close-'+str(i)] = df.close.shift(+i)
#Calculer la valeur moyenne mobile et std,S'il y a même un Nan dans le réglage skipna, il retournera Nan.
nclose = 5
df['MA5'] = df.iloc[:, np.arange(nclose, nclose+5)].mean(axis='columns', skipna=False)
df['MA25'] = df.iloc[:, np.arange(nclose, nclose+25)].mean(axis='columns', skipna=False)
df['MA50'] = df.iloc[:, np.arange(nclose, nclose+50)].mean(axis='columns', skipna=False)
df['MA75'] = df.iloc[:, np.arange(nclose, nclose+75)].mean(axis='columns', skipna=False)
df['STD5'] = df.iloc[:, np.arange(nclose, nclose+5)].std(axis='columns', skipna=False)
df['STD25'] = df.iloc[:, np.arange(nclose, nclose+25)].std(axis='columns', skipna=False)
df['STD50'] = df.iloc[:, np.arange(nclose, nclose+50)].std(axis='columns', skipna=False)
df['STD75'] = df.iloc[:, np.arange(nclose, nclose+75)].std(axis='columns', skipna=False)
#Supprimer les colonnes supplémentaires après le calcul
for i in range(1, 75):
del df['close-'+str(i)]
#Changements par rapport au jour précédent de chaque ligne moyenne (vous pouvez voir si la ligne moyenne mobile est à la hausse ou à la baisse)
#shift(-1)Rapprochez-vous d'un
df['diff_MA5'] = df['MA5'] - df.MA5.shift(1)
df['diff_MA25'] = df['MA25'] - df.MA25.shift(1)
df['diff_MA50'] = df['MA50'] - df.MA50.shift(1)
df['diff_MA75'] = df['MA50'] - df.MA50.shift(1)
#Ouvert il y a jusqu'à 3 jours, close, high,Je veux ajouter du bas à mon identité
for i in range(1, 4):
df['close-'+str(i)] = df.close.shift(+i)
df['open-'+str(i)] = df.open.shift(+i)
df['high-'+str(i)] = df.high.shift(+i)
df['low-'+str(i)] = df.low.shift(+i)
#Supprimer la ligne contenant NaN
df = df.dropna()
#Décidez du nombre de jours à utiliser
nday = 500
df = df[-nday:]
#df.head()
df
Le côté droit est coupé, mais il ressemble à ceci.
--MA est la moyenne mobile. Par exemple, MA5 est le cours de clôture moyen des 5 derniers jours à compter de cette date. --close-n représente le cours de clôture il y a n jours (idem pour open, high et low) --STD est l'écart type ――Je voulais savoir si la moyenne mobile pointe vers le haut ou vers le bas, j'ai donc ajouté le changement par rapport à la veille avec le nom diff_.
Pour le moment, j'ai choisi les données pour 500 jours. Il était correct de le spécifier lors de la première lecture des données, mais lors du calcul de la moyenne sur 75 jours, des données remontant à 75 jours sont nécessaires, donc 500 jours de données seront utilisés une fois le calcul terminé. J'utilise. Vous disposez désormais de 500 lignes et 30 colonnes de données.
Maintenant que nous sommes prêts, divisons-le en train, testons-le et évaluons-le.
n = df.shape[0]
p = df.shape[1]
print(n,p)
#Divisé en données d'entraînement et données de test. Ne pas mélanger
train_start = 0
train_end = int(np.floor(0.8*n))
test_start = train_end + 1
test_end = n
data_train = np.arange(train_start, train_end)
data_train = df.iloc[np.arange(train_start, train_end), :]
data_test = df.iloc[np.arange(test_start, test_end), :]
#Vérifiez la taille des données d'entraînement et des données de test
print(data_train.shape)
print(data_test.shape)
Cette fois, il a été divisé à 8: 2.
(400, 30)
(99, 30)
Ensuite, la partie de l'étiquette de réponse correcte est séparée et l'apprentissage est effectué avec l'arbre de décision. L'hyper paramètre max_depth, qui indique la profondeur de l'arbre, est mis à 5 pour le moment, mais la valeur appropriée sera déterminée par la recherche de grille après cela.
#Cible séparée
X_train = data_train.iloc[:, 1:]
y_train = data_train.iloc[:, 0]
X_test = data_test.iloc[:, 1:]
y_test = data_test.iloc[:, 0]
#Formation du modèle technique décisif
clf_2 = DecisionTreeClassifier(max_depth=5)
L'arbre de décision est enfin sorti. Effectuez une validation croisée et une recherche dans la grille avec k = 10.
#max avec recherche de grille_Déterminez les paramètres optimaux pour la profondeur
#k=Effectuer également la vérification des intersections de division 10 k
params = {'max_depth': [2, 5, 10, 20]}
grid = GridSearchCV(estimator=clf_2,
param_grid=params,
cv=10,
scoring='roc_auc')
grid.fit(X_train, y_train)
for r, _ in enumerate(grid.cv_results_['mean_test_score']):
print("%0.3f +/- %0.2f %r"
% (grid.cv_results_['mean_test_score'][r],
grid.cv_results_['std_test_score'][r] / 2.0,
grid.cv_results_['params'][r]))
print('Best parameters: %s' % grid.best_params_)
print('Accuracy: %.2f' % grid.best_score_)
La sortie ressemble à ceci. Lorsque la profondeur est de 10, le taux de réponse correct est le plus élevé, soit 69%.
0.630 +/- 0.05 {'max_depth': 2}
0.679 +/- 0.06 {'max_depth': 5}
0.690 +/- 0.06 {'max_depth': 10}
0.665 +/- 0.05 {'max_depth': 20}
Best parameters: {'max_depth': 10}
Accuracy: 0.69
Le taux de réponse correct indiqué ci-dessus est simplement le taux de réponse correct dans les données d'entraînement, alors essayons de prédire les données de test.
#Apprenez à utiliser les paramètres les mieux adaptés à la recherche de grille
clf_2 = grid.best_estimator_
clf_2 = clf_2.fit(X_train, y_train)
clf_2
Vous pouvez voir que les paramètres sont définis comme ceci.
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=10,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False,
random_state=None, splitter='best')
Visualisons-le car c'est un gros problème. Puisque max_depth = 10, ce sera un gâchis. .. Si vous ne découpez que la partie supérieure, cela ressemble à ceci. Dans ce cas, on peut voir que le prix haut il y a trois jours est divisé selon qu'il est de 82,068 ou plus ou moins, puis le seuil est déterminé par le prix bas du jour et divisé. Le fait qu'il soit acheté sous forme de gini signifie que la division est effectuée de sorte que la valeur de la pureté de gini devient petite. La valeur = part indique le nombre de montées et de descentes.
Vérifions le taux de réponse correct du test.
pred_test_2 = clf_2.predict(X_test)
#Taux d'exactitude des données de test
accuracy_score(y_test, pred_test_2)
0.555555555555
Hmm. .. C'est comme ... pour tout le travail acharné, mais c'est comme ça. Voyons également quelles fonctionnalités sont importantes (impotance des fonctionnalités).
#Afficher des qualités importantes
importances = clf_2.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(X_train.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30,
df.columns[1+indices[f]],
importances[indices[f]]))
J'ai essayé de calculer la moyenne mobile et ainsi de suite, mais au final, il semble que le mouvement des prix la veille ou la veille soit important.
1) low 0.407248
2) close 0.184738
3) low-1 0.078743
4) high-3 0.069653
5) high 0.043982
6) diff_MA5 0.039119
7) close-3 0.035420
8) STD50 0.035032
9) diff_MA25 0.029473
10) MA75 0.028125
11) MA50 0.009830
12) open-3 0.009540
13) STD25 0.009159
14) low-3 0.007632
15) high-1 0.007632
16) volume 0.004674
Puisqu'il est 0 ci-dessous, il est omis
Résumons à nouveau les conditions de cette vérification
--__ Période d'agrégation : 500 jours jusqu'au 31 décembre 2019 -- Classificateur : Arbre de décision -- paramètre _: max_depth = 10 -- Vérification d'intersection : 10 divisions -- Formation et test fractionné : 8: 2 -- Résultat __: Taux de réponse correcte 0,55 Ici, les paramètres de l'arbre de décision ont été décidés par la recherche de grille, donc j'ai pensé que c'était correct, mais j'ai essayé parce que la période d'agrégation et le rapport de division semblaient être falsifiés.
--_ Résultat de la modification du rapport de division à 9: __ → Taux de réponse correct: __ 61% __ (max_depth = 20) --__ Résultat d'une période totale de 200 jours __ → Taux de réponse correcte: __ 56% __ (max_depth = 10) est devenu. Le taux de réponse correcte est d'environ 50 à 60%. La recherche de grille est refaite à chaque fois que les conditions sont modifiées, mais environ max_depth = 10-20 était le meilleur score.
――Si vous faites de votre mieux pour faire des prédictions basées sur les données quotidiennes du yen dollar, vous obtiendrez une précision d'environ 50 à 60%.
C'est plus long que prévu. .. fin
Recommended Posts