L'inspiration de cet article est venue de mes observations sur certains avertissements et pièges courants après avoir tenté d'appliquer les techniques d'apprentissage automatique aux problèmes de transaction lors de mes recherches sur les plates-formes de quantification d'inventeurs.
Si vous n'avez pas encore lu mon précédent article, nous vous recommandons de lire mon guide sur l'environnement de recherche de données automatisées et les méthodes systématiques pour élaborer des stratégies de trading, qui a été créé sur la plateforme de quantification des inventeurs.
L'adresse est ici:https://www.fmz.com/digest-topic/4187ethttps://www.fmz.com/digest-topic/4169 这两篇文章.
Ce tutoriel est conçu pour les amateurs, les ingénieurs et les scientifiques de données de tous niveaux de compétence, que vous soyez un gros de l'industrie ou un petit programmeur, la seule compétence dont vous avez besoin est une connaissance de base du langage de programmation Python et une connaissance suffisante des opérations de la ligne de commande.
发明者量化平台FMZ.COM除了提供优质的各大主流交易所的数据源,还提供一套丰富的API接口以帮助我们在完成数据的分析后进行自动化交易。这套接口包括查询账户信息,查询各个主流交易所的高,开,低,收价格,成交量,各种常用技术分析指标等实用工具,特别是对于实际交易过程中连接各大主流交易所的公共API接口,提供了强大的技术支持。
Toutes les fonctionnalités mentionnées ci-dessus sont encapsulées dans un système similaire à Docker, et tout ce que nous devons faire est d'acheter ou de louer notre propre service de cloud computing et de le déployer.
Dans le nom officiel de la plate-forme de quantification des inventeurs, ce système Docker est appelé système d'hébergement.
Pour savoir comment déployer des hôtes et des robots, veuillez vous référer à mon précédent article:https://www.fmz.com/bbs-topic/4140
Les lecteurs qui souhaitent acheter leur propre hôte de déploiement de serveur en nuage peuvent consulter cet article:https://www.fmz.com/bbs-topic/2848
Après avoir déployé avec succès de bons services de cloud computing et un système d'administrateur, nous allons installer le plus grand temple de Python à ce jour: Anaconda.
Pour mettre en œuvre tous les environnements de programmation nécessaires à cet article (la bibliothèque de dépendances, la gestion des versions, etc.), le moyen le plus simple est d'utiliser Anaconda. Il s'agit d'un écosystème de data science Python et d'un gestionnaire de bibliothèque de dépendances.
Comme nous installons Anaconda sur un service cloud, nous vous recommandons d'installer une version Anaconda sur un serveur cloud avec une ligne de commande sur le système Linux.
Pour savoir comment installer Anaconda, consultez le guide officiel d'Anaconda:https://www.anaconda.com/distribution/
Si vous êtes un programmeur Python expérimenté et que vous n'avez pas besoin d'utiliser Anaconda, ce n'est pas un problème. Je suppose que vous n'avez pas besoin d'aide pour installer les dépendances nécessaires, vous pouvez simplement sauter cette section.
La sortie finale de la stratégie de transaction doit répondre aux questions suivantes:
Direction: déterminer si l'actif est bon marché, cher ou juste en valeur.
开仓条件:如果资产价格便宜或者昂贵,你应该做多或者做空.
Transaction au guichet unique: si le prix de l'actif est raisonnable et que nous avons une position dans l'actif (achat ou vente antérieur), devriez-vous le négocier au guichet unique?
Plage de prix: le prix (ou la plage) pour la transaction ouverte
Quantité: quantité de fonds négociés (par exemple, quantité de crypto-monnaie ou de contrats à terme sur des produits)
L'apprentissage automatique peut être utilisé pour répondre à chacune des questions ci-dessus, mais pour le reste de cet article, nous allons nous concentrer sur la première question, la direction des transactions.
Il existe deux types d'approches pour construire des stratégies, l'une basée sur les modèles et l'autre basée sur l'exploration des données.
Dans la construction de stratégies basées sur des modèles, nous commençons par un modèle d'inefficacité du marché, construisant des expressions mathématiques (par exemple, prix, rendement) et testant leur efficacité sur des cycles de temps plus longs. Le modèle est généralement une version simplifiée d'un modèle réellement complexe, qui nécessite de vérifier sa signification et sa stabilité sur des cycles plus longs.
D'un autre côté, nous cherchons d'abord des modèles de prix et essayons d'utiliser des algorithmes dans les méthodes d'exploration de données. Les raisons pour lesquelles ces modèles se produisent n'ont pas d'importance, car seuls des modèles déterminés continueront à se répéter à l'avenir. C'est une méthode d'analyse aveugle qui nécessite un examen rigoureux pour identifier les vrais modèles à partir de modèles aléatoires.
De toute évidence, l'apprentissage automatique peut facilement être appliqué à des méthodes d'extraction de données. Voyons comment utiliser l'apprentissage automatique pour créer des signaux de transaction à partir de l'extraction de données.
L'exemple de code utilise des outils de retouche basés sur la plate-forme de quantification des inventeurs et une interface API d'automatisation des transactions. Après avoir déployé le gestionnaire et installé Anaconda dans la section ci-dessus, il vous suffit d'installer la bibliothèque d'analyse de la science des données dont nous avons besoin ainsi que le célèbre modèle d'apprentissage automatique scikit-learn.
pip install -U scikit-learn
Avant de commencer, un système de problèmes d'apprentissage automatique standard est illustré ci-dessous:
Système de problèmes d'apprentissage automatique
Les caractéristiques que nous allons créer doivent avoir une certaine capacité de prédiction (X), nous voulons prédire la variable cible (Y) et utiliser les données historiques pour former un modèle ML qui peut prédire Y le plus près possible de la valeur réelle. Enfin, nous utilisons ce modèle pour prédire de nouvelles données inconnues sur Y. Cela nous amène à la première étape:
C'est-à-dire, dans le cadre que nous avons ci-dessus, qu'est-ce que Y?
Qu'est-ce que vous voulez prévoir?
Voulez-vous prédire les prix futurs, les rendements/PNL futurs, des signaux d'achat/vente, optimiser l'allocation de votre portefeuille et essayer d'exécuter vos transactions efficacement?
Supposons que nous essayions de prédire le prix sur la prochaine période. Dans ce cas, Y ((t) = prix ((t + 1)). Nous pouvons maintenant compléter notre cadre avec des données historiques.
Notez que Y (t) est connu uniquement dans la rétrospective, mais que lorsque nous utilisons notre modèle, nous ne connaîtrons pas le prix du temps t (t + 1) ; nous utilisons notre modèle pour prédire Y (t) et ne le comparons à la valeur réelle qu'au temps t + 1; cela signifie que vous ne pouvez pas utiliser Y comme caractéristique dans le modèle de prédit.
Une fois que nous connaissons l'objectif Y, nous pouvons également décider comment évaluer nos prédictions. Ceci est très important pour distinguer les différents modèles de données que nous allons essayer. En fonction du problème que nous résolvons, choisissons un indicateur qui mesure l'efficacité de notre modèle. Par exemple, si nous prévoyons un prix, nous pouvons utiliser l'erreur de racine orthogonale comme indicateur.
Cadre ML utilisé pour prédire les prix futurs
Pour la démonstration, nous allons créer un modèle de prévision pour prédire la valeur future de la base de référence d'un investissement hypothétique, où:
basis = Price of Stock — Price of Future
basis(t)=S(t)−F(t)
Y(t) = future expected value of basis = Average(basis(t+1),basis(t+2),basis(t+3),basis(t+4),basis(t+5))
Comme il s'agit d'un problème de régression, nous allons évaluer le modèle sur RMSE (erreur de racine carrée); nous allons également utiliser Total Pnl comme critère d'évaluation.
Remarque: Pour les connaissances mathématiques relatives à RMSE, veuillez consulter l'Encyclopédie.
Rassembler et nettoyer les données qui peuvent vous aider à résoudre les problèmes que vous rencontrez
Si nous prévoyons le prix, vous pouvez utiliser les données de prix de l'indicateur d'investissement, les données de volume des transactions de l'indicateur d'investissement, les données similaires de l'indicateur d'investissement concerné, l'indicateur de l'indicateur d'investissement, l'indicateur du marché global de l'équité, les prix des autres actifs concernés, etc.
Vous devez configurer des autorisations d'accès à ces données et vous assurer que vos données sont exactes et sans erreur, et résoudre les données manquantes (problèmes très courants); tout en veillant à ce que vos données soient impartiales et représentent pleinement toutes les conditions du marché (par exemple, le même nombre de scénarios de gains et pertes) afin d'éviter toute déviation dans le modèle. Vous pouvez également avoir besoin de nettoyer les données pour obtenir des dividendes, une fraction de l'indice d'investissement, une suite, etc.
Si vous utilisez une plateforme de quantification d'inventeurs,FMZ.COMNous avons accès à des données globales gratuites de Google, Yahoo, NSE et Quandl; à des données en profondeur sur les futures de produits nationaux tels que CTP et EOS; et à des données sur les principaux échanges de devises numériques tels que Binance, OKEX, Huobi et BitMex. La plate-forme de quantification des inventeurs nettoie et filtre à l'avance ces données, telles que les fractions des indices d'investissement et les données de marché en profondeur, et les présente aux développeurs de stratégies dans un format facile à comprendre pour les travailleurs quantifiés.
Pour faciliter la démonstration de cet article, nous avons utilisé les données suivantes comme indice d'investissement virtuel pour les boules MQK, et nous avons également utilisé un outil de quantification très pratique appelé Auquan Boules Toolbox, pour plus d'informations, voir:https://github.com/Auquan/auquan-toolbox-python
# Load the data
from backtester.dataSource.quant_quest_data_source import QuantQuestDataSource
cachedFolderName = '/Users/chandinijain/Auquan/qq2solver-data/historicalData/'
dataSetId = 'trainingData1'
instrumentIds = ['MQK']
ds = QuantQuestDataSource(cachedFolderName=cachedFolderName,
dataSetId=dataSetId,
instrumentIds=instrumentIds)
def loadData(ds):
data = None
for key in ds.getBookDataByFeature().keys():
if data is None:
data = pd.DataFrame(np.nan, index = ds.getBookDataByFeature()[key].index, columns=[])
data[key] = ds.getBookDataByFeature()[key]
data['Stock Price'] = ds.getBookDataByFeature()['stockTopBidPrice'] + ds.getBookDataByFeature()['stockTopAskPrice'] / 2.0
data['Future Price'] = ds.getBookDataByFeature()['futureTopBidPrice'] + ds.getBookDataByFeature()['futureTopAskPrice'] / 2.0
data['Y(Target)'] = ds.getBookDataByFeature()['basis'].shift(-5)
del data['benchmark_score']
del data['FairValue']
return data
data = loadData(ds)
Grâce au code ci-dessus, Auquan's Toolbox a téléchargé et chargé les données dans le dictionnaire du dictionnaire du dictionnaire. Nous devons maintenant préparer les données dans le format que nous aimons.
C'est une étape très importante!Avant de continuer, nous devrions diviser les données en ensembles de données de formation pour former votre modèle; ensembles de données de test pour évaluer la performance du modèle. Il est recommandé de diviser les données en: ensembles de formation de 60 à 70% et ensembles de test de 30 à 40%
Séparer les données en ensembles de formation et en ensembles de test
Étant donné que les données de formation sont utilisées pour évaluer les paramètres du modèle, votre modèle peut être trop adapté à ces données de formation et les données de formation peuvent induire en erreur les performances du modèle. Si vous ne conservez aucune donnée de test séparée et n'utilisez pas toutes les données pour la formation, vous ne saurez pas si votre modèle fonctionne bien ou mal avec les nouvelles données invisibles. C'est l'une des principales raisons pour lesquelles les modèles ML formés échouent avec des données en temps réel: les gens sont formés avec toutes les données disponibles et sont excités par les indicateurs de formation, mais le modèle ne peut pas faire de prédictions significatives sur les données en temps réel non formées.
Séparer les données en ensembles de formation, de vérification et de test
Cette approche pose un problème. Si nous entraînons les données de formation, évaluons la performance des données de test et optimisons notre modèle jusqu'à ce que nous soyons satisfaits de la performance, nous faisons implicitement partie des données de test. En fin de compte, notre modèle peut bien fonctionner sur ce groupe de données de formation et de test, mais il n'est pas garanti qu'il prédira bien les nouvelles données.
Pour résoudre ce problème, nous pouvons créer un ensemble de données de vérification séparé. Maintenant, vous pouvez entraîner les données, évaluer la performance des données de vérification, optimiser jusqu'à ce que vous soyez satisfait de la performance, et finalement tester les données de test.
N'oubliez pas de ne pas revenir en arrière et d'essayer d'optimiser davantage le modèle une fois que vous avez examiné la performance des données de test. Si vous trouvez que votre modèle ne donne pas de bons résultats, jetez-le complètement et recommencez. Il est recommandé de diviser 60% des données de formation, 20% des données de vérification et 20% des données de test.
Pour notre problème, nous avons trois ensembles de données disponibles, nous allons utiliser l'un comme ensemble de formation, le second comme ensemble de vérification, et le troisième comme notre ensemble de test.
# Training Data
dataSetId = 'trainingData1'
ds_training = QuantQuestDataSource(cachedFolderName=cachedFolderName,
dataSetId=dataSetId,
instrumentIds=instrumentIds)
training_data = loadData(ds_training)
# Validation Data
dataSetId = 'trainingData2'
ds_validation = QuantQuestDataSource(cachedFolderName=cachedFolderName,
dataSetId=dataSetId,
instrumentIds=instrumentIds)
validation_data = loadData(ds_validation)
# Test Data
dataSetId = 'trainingData3'
ds_test = QuantQuestDataSource(cachedFolderName=cachedFolderName,
dataSetId=dataSetId,
instrumentIds=instrumentIds)
out_of_sample_test_data = loadData(ds_test)
Pour chacune d'elles, nous ajoutons la variable cible Y, définie comme la moyenne des cinq bases suivantes.
def prepareData(data, period):
data['Y(Target)'] = data['basis'].rolling(period).mean().shift(-period)
if 'FairValue' in data.columns:
del data['FairValue']
data.dropna(inplace=True)
period = 5
prepareData(training_data, period)
prepareData(validation_data, period)
prepareData(out_of_sample_test_data, period)
Analyser le comportement des données et créer des caractéristiques prédictives
La vraie ingénierie commence à construire dès maintenant. La règle d'or de la sélection des caractéristiques est que la capacité de prédiction provient principalement des caractéristiques, et non du modèle. Vous constaterez que la sélection des caractéristiques a un impact beaucoup plus important sur les performances que la sélection du modèle. Quelques notes sur la sélection des caractéristiques:
Ne choisissez pas un ensemble de caractéristiques au hasard sans explorer la relation avec la variable cible.
Peu ou pas de rapport avec les variables cibles peut entraîner une suradaptation
Les caractéristiques que vous choisissez peuvent être fortement liées les unes aux autres, dans ce cas, un petit nombre de caractéristiques peut également expliquer l'objectif
Je crée généralement des caractéristiques intuitives pour voir comment les variables cibles sont liées à ces caractéristiques et comment elles sont liées entre elles pour décider quelles variables utiliser.
Vous pouvez également essayer de trier les caractéristiques des candidats en fonction du coefficient d'information le plus élevé (MIC), en effectuant l'analyse des composants principaux (PCA) et d'autres méthodes.
Les modèles ML ont tendance à bien fonctionner en termes de standardisation. Cependant, la normalisation est difficile lorsqu'il s'agit de traiter des données de séquence temporelle, car la portée future des données est inconnue. Vos données peuvent dépasser la portée standardisée, ce qui entraîne des erreurs de modèle.
Scalation: caractéristiques classées selon une plage de décalage standard ou de quatre chiffres
Résidence: moins la moyenne historique par rapport à la valeur actuelle
Unification: les deux périodes rétrospectives de ((x - mean) / stdev ci-dessus
Homogénéisation régulière: standardisation des données dans la plage de -1 à +1 et redéfinition du centre au cours de la période de rétroaction ((x-min) / ((max-min))
Notez que, comme nous utilisons des moyennes historiques continues, des écarts standards, des valeurs maximales ou minimales au-delà de la période rétrospective, les valeurs standardisées de l'attribution des caractéristiques seront représentées à des valeurs réelles différentes à des moments différents. Par exemple, si la valeur actuelle de la caractéristique est 5, la moyenne de 30 cycles consécutifs est de 4.5, elle sera convertie en 0.5 après le séjour; après cela, si la moyenne de 30 cycles consécutifs devient 3, la valeur de 3.5 deviendra 0.5 ; cela peut être la cause de l'erreur du modèle.
Pour la première itération de notre problème, nous avons créé un grand nombre de caractéristiques avec des paramètres mixtes. Nous allons essayer de voir si nous pouvons réduire le nombre de caractéristiques plus tard.
def difference(dataDf, period):
return dataDf.sub(dataDf.shift(period), fill_value=0)
def ewm(dataDf, halflife):
return dataDf.ewm(halflife=halflife, ignore_na=False,
min_periods=0, adjust=True).mean()
def rsi(data, period):
data_upside = data.sub(data.shift(1), fill_value=0)
data_downside = data_upside.copy()
data_downside[data_upside > 0] = 0
data_upside[data_upside < 0] = 0
avg_upside = data_upside.rolling(period).mean()
avg_downside = - data_downside.rolling(period).mean()
rsi = 100 - (100 * avg_downside / (avg_downside + avg_upside))
rsi[avg_downside == 0] = 100
rsi[(avg_downside == 0) & (avg_upside == 0)] = 0
return rsi
def create_features(data):
basis_X = pd.DataFrame(index = data.index, columns = [])
basis_X['mom3'] = difference(data['basis'],4)
basis_X['mom5'] = difference(data['basis'],6)
basis_X['mom10'] = difference(data['basis'],11)
basis_X['rsi15'] = rsi(data['basis'],15)
basis_X['rsi10'] = rsi(data['basis'],10)
basis_X['emabasis3'] = ewm(data['basis'],3)
basis_X['emabasis5'] = ewm(data['basis'],5)
basis_X['emabasis7'] = ewm(data['basis'],7)
basis_X['emabasis10'] = ewm(data['basis'],10)
basis_X['basis'] = data['basis']
basis_X['vwapbasis'] = data['stockVWAP']-data['futureVWAP']
basis_X['swidth'] = data['stockTopAskPrice'] -
data['stockTopBidPrice']
basis_X['fwidth'] = data['futureTopAskPrice'] -
data['futureTopBidPrice']
basis_X['btopask'] = data['stockTopAskPrice'] -
data['futureTopAskPrice']
basis_X['btopbid'] = data['stockTopBidPrice'] -
data['futureTopBidPrice']
basis_X['totalaskvol'] = data['stockTotalAskVol'] -
data['futureTotalAskVol']
basis_X['totalbidvol'] = data['stockTotalBidVol'] -
data['futureTotalBidVol']
basis_X['emabasisdi7'] = basis_X['emabasis7'] -
basis_X['emabasis5'] +
basis_X['emabasis3']
basis_X = basis_X.fillna(0)
basis_y = data['Y(Target)']
basis_y.dropna(inplace=True)
print("Any null data in y: %s, X: %s"
%(basis_y.isnull().values.any(),
basis_X.isnull().values.any()))
print("Length y: %s, X: %s"
%(len(basis_y.index), len(basis_X.index)))
return basis_X, basis_y
basis_X_train, basis_y_train = create_features(training_data)
basis_X_test, basis_y_test = create_features(validation_data)
Sélectionnez le modèle statistique/ML approprié en fonction de la question choisie
Le choix du modèle dépend de la façon dont le problème est construit. Êtes-vous en train de résoudre un problème de surveillance (tout point X dans la matrice de caractéristiques est mappé à la variable cible Y) ou un problème d'apprentissage non supervisé (pas de mappage donné, le modèle essaie d'apprendre un modèle inconnu)? Êtes-vous en train de résoudre un problème de régression (prédire le prix réel à l'avenir) ou un problème de classification (prédire uniquement la direction des prix à l'avenir) (augmenter/diminuer))
Apprentissage supervisé ou non supervisé
Retour ou catégorisation
Voici quelques algorithmes d'apprentissage supervisé courants qui peuvent vous aider:
Régression linéaire (paramètres, régression)
Régression logistique (paramètres, classement)
Algorithme de proximité (KNN) basé sur des exemples, régression
SVM, SVR (paramètres, classement et régression)
Arbre de décision
La forêt décisionnelle
Je recommande de commencer par un modèle simple, comme une rétrogradation linéaire ou logique, et de construire un modèle plus complexe à partir de là selon les besoins. Je recommande également de lire les mathématiques derrière le modèle plutôt que de l'utiliser aveuglément comme une boîte noire.
Formez et optimisez vos modèles avec des ensembles de données de formation et de validation
À ce stade, vous êtes prêt à construire le modèle final. À ce stade, vous ne faites que répéter le modèle et les paramètres du modèle. Vous entraînez votre modèle sur les données de formation, mesurez sa performance sur les données de vérification, puis revenez, optimisez, réentraînez et évaluez.
Il est important que vous ayez un modèle que vous aimez et que vous puissiez passer à l'étape suivante.
Pour notre problème de démonstration, commençons par une simple régression linéaire.
from sklearn import linear_model
from sklearn.metrics import mean_squared_error, r2_score
def linear_regression(basis_X_train, basis_y_train,
basis_X_test,basis_y_test):
regr = linear_model.LinearRegression()
# Train the model using the training sets
regr.fit(basis_X_train, basis_y_train)
# Make predictions using the testing set
basis_y_pred = regr.predict(basis_X_test)
# The coefficients
print('Coefficients: \n', regr.coef_)
# The mean squared error
print("Mean squared error: %.2f"
% mean_squared_error(basis_y_test, basis_y_pred))
# Explained variance score: 1 is perfect prediction
print('Variance score: %.2f' % r2_score(basis_y_test,
basis_y_pred))
# Plot outputs
plt.scatter(basis_y_pred, basis_y_test, color='black')
plt.plot(basis_y_test, basis_y_test, color='blue', linewidth=3)
plt.xlabel('Y(actual)')
plt.ylabel('Y(Predicted)')
plt.show()
return regr, basis_y_pred
_, basis_y_pred = linear_regression(basis_X_train, basis_y_train,
basis_X_test,basis_y_test)
Il n'y a pas de régression linéaire homogène
('Coefficients: \n', array([ -1.0929e+08, 4.1621e+07, 1.4755e+07, 5.6988e+06, -5.656e+01, -6.18e-04, -8.2541e-05,4.3606e-02, -3.0647e-02, 1.8826e+07, 8.3561e-02, 3.723e-03, -6.2637e-03, 1.8826e+07, 1.8826e+07, 6.4277e-02, 5.7254e-02, 3.3435e-03, 1.6376e-02, -7.3588e-03, -8.1531e-04, -3.9095e-02, 3.1418e-02, 3.3321e-03, -1.3262e-06, -1.3433e+07, 3.5821e+07, 2.6764e+07, -8.0394e+06, -2.2388e+06, -1.7096e+07]))
Mean squared error: 0.02
Variance score: 0.96
Regardez les coefficients du modèle. Nous ne pouvons pas vraiment les comparer ou dire ce qui est important, car ils appartiennent à des échelles différentes. Essayons d'unifier pour qu'ils correspondent aux mêmes proportions et imposons également une certaine stabilité.
def normalize(basis_X, basis_y, period):
basis_X_norm = (basis_X - basis_X.rolling(period).mean())/
basis_X.rolling(period).std()
basis_X_norm.dropna(inplace=True)
basis_y_norm = (basis_y -
basis_X['basis'].rolling(period).mean())/
basis_X['basis'].rolling(period).std()
basis_y_norm = basis_y_norm[basis_X_norm.index]
return basis_X_norm, basis_y_norm
norm_period = 375
basis_X_norm_test, basis_y_norm_test = normalize(basis_X_test,basis_y_test, norm_period)
basis_X_norm_train, basis_y_norm_train = normalize(basis_X_train, basis_y_train, norm_period)
regr_norm, basis_y_pred = linear_regression(basis_X_norm_train, basis_y_norm_train, basis_X_norm_test, basis_y_norm_test)
basis_y_pred = basis_y_pred * basis_X_test['basis'].rolling(period).std()[basis_y_norm_test.index] + basis_X_test['basis'].rolling(period).mean()[basis_y_norm_test.index]
Retour linéaire de l'unification
Mean squared error: 0.05
Variance score: 0.90
Le modèle n'est pas amélioré par le modèle précédent, mais il n'est pas pire. Nous pouvons maintenant comparer les coefficients pour voir quels coefficients sont vraiment importants.
Voyons les coefficients.
for i in range(len(basis_X_train.columns)):
print('%.4f, %s'%(regr_norm.coef_[i], basis_X_train.columns[i]))
Le résultat:
19.8727, emabasis4
-9.2015, emabasis5
8.8981, emabasis7
-5.5692, emabasis10
-0.0036, rsi15
-0.0146, rsi10
0.0196, mom10
-0.0035, mom5
-7.9138, basis
0.0062, swidth
0.0117, fwidth
2.0883, btopask
2.0311, btopbid
0.0974, bavgask
0.0611, bavgbid
0.0007, topaskvolratio
0.0113, topbidvolratio
-0.0220, totalaskvolratio
0.0231, totalbidvolratio
Nous pouvons voir clairement que certaines caractéristiques ont des coefficients plus élevés que d'autres et peuvent être plus prédictives.
Nous allons voir comment les différentes caractéristiques sont liées.
import seaborn
c = basis_X_train.corr()
plt.figure(figsize=(10,10))
seaborn.heatmap(c, cmap='RdYlGn_r', mask = (np.abs(c) <= 0.8))
plt.show()
Corrélation entre les caractéristiques
Les zones en rouge foncé indiquent les variables hautement pertinentes.
例如,我可以轻松地丢弃像emabasisdi7这样的特征,这些特征只是其他特征的线性组合.
def create_features_again(data):
basis_X = pd.DataFrame(index = data.index, columns = [])
basis_X['mom10'] = difference(data['basis'],11)
basis_X['emabasis2'] = ewm(data['basis'],2)
basis_X['emabasis5'] = ewm(data['basis'],5)
basis_X['emabasis10'] = ewm(data['basis'],10)
basis_X['basis'] = data['basis']
basis_X['totalaskvolratio'] = (data['stockTotalAskVol']
- data['futureTotalAskVol'])/
100000
basis_X['totalbidvolratio'] = (data['stockTotalBidVol']
- data['futureTotalBidVol'])/
100000
basis_X = basis_X.fillna(0)
basis_y = data['Y(Target)']
basis_y.dropna(inplace=True)
return basis_X, basis_y
basis_X_test, basis_y_test = create_features_again(validation_data)
basis_X_train, basis_y_train = create_features_again(training_data)
_, basis_y_pred = linear_regression(basis_X_train, basis_y_train, basis_X_test,basis_y_test)
basis_y_regr = basis_y_pred.copy()
('Coefficients: ', array([ 0.03246139,
0.49780982, -0.22367172, 0.20275786, 0.50758852,
-0.21510795, 0.17153884]))
Mean squared error: 0.02
Variance score: 0.96
Voyez, les performances de notre modèle n'ont pas changé, nous avons seulement besoin de quelques caractéristiques pour expliquer nos variables cibles. Je vous suggère d'essayer plus de ces caractéristiques, d'essayer de nouvelles combinaisons, etc., pour voir ce qui peut améliorer notre modèle.
我们还可以尝试更复杂的模型,看看模型的变化是否可以提高性能.
from sklearn import neighbors
n_neighbors = 5
model = neighbors.KNeighborsRegressor(n_neighbors, weights='distance')
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_knn = basis_y_pred.copy()
from sklearn.svm import SVR
model = SVR(kernel='rbf', C=1e3, gamma=0.1)
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_svr = basis_y_pred.copy()
model=ensemble.ExtraTreesRegressor()
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_trees = basis_y_pred.copy()
Vérifiez les performances des données d'échantillons réels
Performance de retouche sur les ensembles de données testés (pas encore touchés)
C'est le moment crucial. Nous avons commencé à exécuter notre modèle d'optimisation final à partir de la dernière étape des données de test, et nous l'avons mis de côté au début, les données qui n'ont pas encore été touchées.
Cela vous donne des attentes réalistes sur la façon dont votre modèle va fonctionner sur des données nouvelles et invisibles lorsque vous commencez à trader en temps réel. Il est donc nécessaire de vous assurer que vous avez un ensemble de données propre qui n'est pas utilisé pour former ou vérifier le modèle.
Si vous n'aimez pas le résultat de la retouche des données de test, vous pouvez abandonner le modèle et recommencer. Ne revenez pas à réoptimiser votre modèle, cela conduira à un sur-ajustement!
Nous allons encore utiliser la boîte à outils Auquan.
import backtester
from backtester.features.feature import Feature
from backtester.trading_system import TradingSystem
from backtester.sample_scripts.fair_value_params import FairValueTradingParams
class Problem1Solver():
def getTrainingDataSet(self):
return "trainingData1"
def getSymbolsToTrade(self):
return ['MQK']
def getCustomFeatures(self):
return {'my_custom_feature': MyCustomFeature}
def getFeatureConfigDicts(self):
expma5dic = {'featureKey': 'emabasis5',
'featureId': 'exponential_moving_average',
'params': {'period': 5,
'featureName': 'basis'}}
expma10dic = {'featureKey': 'emabasis10',
'featureId': 'exponential_moving_average',
'params': {'period': 10,
'featureName': 'basis'}}
expma2dic = {'featureKey': 'emabasis3',
'featureId': 'exponential_moving_average',
'params': {'period': 3,
'featureName': 'basis'}}
mom10dic = {'featureKey': 'mom10',
'featureId': 'difference',
'params': {'period': 11,
'featureName': 'basis'}}
return [expma5dic,expma2dic,expma10dic,mom10dic]
def getFairValue(self, updateNum, time, instrumentManager):
# holder for all the instrument features
lbInstF = instrumentManager.getlookbackInstrumentFeatures()
mom10 = lbInstF.getFeatureDf('mom10').iloc[-1]
emabasis2 = lbInstF.getFeatureDf('emabasis2').iloc[-1]
emabasis5 = lbInstF.getFeatureDf('emabasis5').iloc[-1]
emabasis10 = lbInstF.getFeatureDf('emabasis10').iloc[-1]
basis = lbInstF.getFeatureDf('basis').iloc[-1]
totalaskvol = lbInstF.getFeatureDf('stockTotalAskVol').iloc[-1] - lbInstF.getFeatureDf('futureTotalAskVol').iloc[-1]
totalbidvol = lbInstF.getFeatureDf('stockTotalBidVol').iloc[-1] - lbInstF.getFeatureDf('futureTotalBidVol').iloc[-1]
coeff = [ 0.03249183, 0.49675487, -0.22289464, 0.2025182, 0.5080227, -0.21557005, 0.17128488]
newdf['MQK'] = coeff[0] * mom10['MQK'] + coeff[1] * emabasis2['MQK'] +\
coeff[2] * emabasis5['MQK'] + coeff[3] * emabasis10['MQK'] +\
coeff[4] * basis['MQK'] + coeff[5] * totalaskvol['MQK']+\
coeff[6] * totalbidvol['MQK']
newdf.fillna(emabasis5,inplace=True)
return newdf
problem1Solver = Problem1Solver()
tsParams = FairValueTradingParams(problem1Solver)
tradingSystem = TradingSystem(tsParams)
tradingSystem.startTrading(onlyAnalyze=False,
shouldPlot=True,
makeInstrumentCsvs=False)
Résultats des tests, Pnl calculé en dollars (Pnl n'inclut pas les frais de transaction et autres frais)
Vérification en défilement, apprentissage collectif, Bagging et Boosting
En plus de collecter plus de données, de créer de meilleures fonctionnalités ou d'essayer plus de modèles, il y a quelques choses que vous pouvez essayer d'améliorer.
1. Vérification en défilement
Vérification par défilement
Les conditions du marché restent rarement les mêmes. Supposons que vous ayez des données pour un an et que vous vous entraîniez avec des données de janvier à août et que vous testiez votre modèle avec des données de septembre à décembre. Vous finirez peut-être par vous entraîner contre un ensemble de conditions de marché très spécifiques.
Il est peut-être préférable d'essayer une vérification en défilement, avec une formation de janvier à février, une vérification de mars, une re-formation d'avril à mai, une vérification de juin, etc.
2. L'apprentissage collectif
L'apprentissage collectif
Certains modèles peuvent être très efficaces pour prédire certains scénarios, tandis que d'autres peuvent être extrêmement mauvais pour certains scénarios ou pour certains cas. Une méthode pour réduire les erreurs et les mauvais ajustements est d'utiliser des ensembles de différents modèles. Votre prévision sera une moyenne des prédictions faites par de nombreux modèles, et les erreurs des différents modèles peuvent être compensées ou réduites.
Emportement en sac
Le renforcement
Pour une brève introduction, je vais sauter ces méthodes, mais vous pouvez trouver plus d'informations en ligne.
Nous allons essayer une approche de l'ensemble pour notre problème.
basis_y_pred_ensemble = (basis_y_trees + basis_y_svr +
basis_y_knn + basis_y_regr)/4
Mean squared error: 0.02
Variance score: 0.95
Comment résoudre vos problèmes
Rassembler des données fiables et les nettoyer
Séparer les données en ensembles de formation, de vérification et de test
Créer des traits et analyser leur comportement
Choisir le modèle de formation approprié en fonction du comportement
Utilisez les données de formation pour former vos modèles et faire des prédictions.
Vérifiez et réoptimisez les performances des ensembles de vérifications
Vérifier les performances finales du test
C'est un bon début, n'est-ce pas? Mais ce n'est pas fini, vous n'avez plus qu'un modèle de prédiction fiable.
Développer des signaux basés sur des modèles prédictifs pour identifier la direction des transactions
Développer des stratégies spécifiques pour identifier les positions ouvertes
Système d'exécution pour identifier les positions et les prix
Les inventeurs ont besoin de plus de plates-formes de quantification.FMZ.COMLa plate-forme d'inventeur quantique propose des stratégies alternatives très développées, avec des méthodes d'apprentissage automatique, qui vous permettront d'ajouter des stratégies spécifiques telles que Tiger Wing.https://www.fmz.com/square
Des informations importantes sur le coût des transactions:你的模型会告诉你所选资产何时是做多或做空。然而,它没有考虑费用/交易成本/可用交易量/止损等。交易成本通常会使有利可图的交易成为亏损。例如,预期价格上涨0.05美元的资产是买入,但如果你必须支付0.10美元进行此交易,你将最终获得净亏损$0.05。在你考虑经纪人佣金,交换费和点差后,我们上面看起来很棒的盈利图实际上是这样的:
Les résultats de la retouche après frais de transaction et décalage, Pnl en dollars
Les frais de transaction et les écarts représentent plus de 90% de notre PNL! Nous en parlerons plus en détail dans les articles suivants.
Pour finir, nous allons voir quelques-uns des pièges courants.
Ne vous y trompez pas trop!
Ne pas re-entraîner après chaque point de données: c'est une erreur courante dans le développement de l'apprentissage automatique. Si votre modèle doit être re-entraîné après chaque point de données, il n'est probablement pas un très bon modèle. C'est-à-dire qu'il doit être réentraîné régulièrement et seulement à une fréquence raisonnable.
Évitez les écarts, en particulier les écarts prospectifs: c'est une autre raison pour laquelle les modèles ne fonctionnent pas, assurez-vous que vous n'utilisez aucune information future. Dans la plupart des cas, cela signifie que vous ne devez pas utiliser la variable cible Y comme caractéristique dans le modèle. Vous pouvez l'utiliser pendant les tests rétrospectifs, mais ce ne sera pas possible lors de l'exécution réelle du modèle, ce qui rendra votre modèle inutilisable.
Attention aux déviations de l'exploration des données: comme nous essayons de modéliser une série de nos données pour déterminer si elles conviennent, assurez-vous de faire des tests stricts pour séparer les modèles aléatoires des modèles réels qui peuvent se produire, à moins qu'il n'y ait une raison particulière. Par exemple, la régression linéaire explique bien les modèles d'une tendance ascendante, qui sera probablement une fraction de la plus grande vague aléatoire!
Je pense que c'est très important, et je pense qu'il faut le répéter.
L'hyperconformité est le piège le plus dangereux de la stratégie de trading.
Un algorithme complexe peut être très performant dans les retrospectives, mais il échoue malheureusement sur de nouvelles données invisibles, qui ne révèlent pas vraiment de tendances dans les données et n'ont pas la capacité de faire de vraies prédictions.
Restez aussi simple que possible. Si vous trouvez que vous avez besoin de nombreuses fonctions complexes pour interpréter les données, vous risquez d'être trop adapté.
Divisez vos données disponibles en données de formation et de test et vérifiez toujours la performance des données d'échantillon réelles avant d'utiliser le modèle pour effectuer des transactions en temps réel.
Une feuille de savoirMerci mon père.
congcong009Un excellent article, des idées et des résumés pour les débutants.
laladémaxieIl est trop fort!