Fordelen ved machine learning er, at det er nemt at inkorpurere big data. Eksempelvis kan man trawle nyhedssites som Hellerupfinans.dk og andre finansielle udgivelser for positive/negative omtalelses, der ofte påvirker kurserne.
I dette eksempel bruger vi python. Vi vil desuden tage udgangspunkt i VIX indekset, som er et volatilitets indekset for S&P500. Kan for forecaste indekset udvikling kan man tjene penge. Mange penge!
Først installere vi de pakker, som skal hjælpe os med databearbejdningen. (Du kan læse mere om der forskellige pakker her)
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import keras
from sklearn.preprocessing import MinMaxScaler
Nu kommer vi til LSTM pakkerne, hvorfor i kommer til at eske Python. Bibloteket hedder Keras og har alt hvad vi skal bruge, nemt og brugervenligt.
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dropout
from keras import optimizers
from keras.layers import Dense, Dropout, Activation
Nu importere dataen og for simpelhedens skyld fodre vi kun netværket med lukkekursen, selvom det klart er en fordel at smide så meget data ind som overhovedet muligt. Ved brug af describe funktionen, kan man hurtigt skabe et overblik over det data man arbejder med.
Husk at splitte data op i et træningssæt og et træningssæt og testsæt.
VIX_training_complete = pd.read_csv(‘data.csv’)
VIX_training_processed = VIX_training_complete.iloc[:, 4:5].values
VIX_training_complete[[‘VIX Close’]].describe()
Lad os for en god ordens skyld også lige illustrere dataen, så vi ved hvad vi arbejder med.
Vi normalisere nu data til mellem 0 og 1
scaler = MinMaxScaler(feature_range = (0, 1))
VIX_training_scaled = scaler.fit_transform(VIX_training_processed)
Vi indstiller nu netværket således at kursen på tisdpunkt t+1 bestemmes ud fra de 50 tidligere observationer. Datasættet som jeg har benyttes består af ialt 3780 observationer.
features_set = []
labels = []
for i in range(50, 3730):
features_set.append(VIX_training_scaled[i-50:i, 0])
labels.append(VIX_training_scaled[i, 0])
Data outputtet skal stå i det rigtige vetorformat, og da vi kun forcaster en dag frem indstilles output til én dimension.
features_set, labels = np.array(features_set), np.array(labels)
features_set = np.reshape(features_set, (features_set.shape[0], features_set.shape[1], 1))
model = Sequential()
Nu tilføjer vi LSTM cellerne med 50 neuroner i hver. Den første parameter i input_shape angiver antallet af tidsteps, mens det sidste 1 tal er angiver antaller af indikatorer (Close price). Derudover fjerne vi nogle af observationerne for at ungå overidentifikation. Vi kan f.eks. sætte dropout til 20 pct., hvilket betyder at 20 pct. af observationer frasorteres. (Du kan læse mere om overidentifikation her).
num_units = 50
model.add(LSTM(units=num_units, return_sequences=True, input_shape=(features_set.shape[1], 1)))
model.add(Dropout(0.2))
model.add(LSTM(units=num_units, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(units=num_units, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(units=num_units))
model.add(Dropout(0.2))
model.add(Dense(units = 1))
For at gøre modellen mere robust tilføjes et “dence” lag i slutnignen af modellen.
Nu da vi har opbygget netværket, er vi klar til den mere sjove del – optimering. Der findes adskillige optimeringsalgoritmer, men ADAM-algoritmen har vist sig at være utrolig præcis til netop denne opgave. Du kan læse mere her om de forkellige optimeringsalgoritmer.
Nu har du muligheden for selv at vælge mellem to koder afhængig af dit tidligere kendskab til algoritmen. Nummer #1 er en standard indstillet algoritme, som er indstillet efter et gennemsnitligt optimum. Desuden benyttes omkostningsfunktionen mean squared error (MSE), som matematisk udtrykker den kvardreret gennemsnitlige forskel mellem den fremskrevet værdi og den sande værdi: \mathrm{MSE}=\frac{1}{n} \sum_{i=1}^{n}\left(Y_{i}-\hat{Y}_{i}\right)^{2}. Nogle gange kan man udelede et bedre resultat ved at indstille på hyperparameterne i ADAM-algoritmen, hvilket #2 kan benyttes til. Her kan man skrue op og ned for f.eks. indlæringsraten.
#1
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
#2
adam1 = keras.optimizers.Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.00, amsgrad=False)
model.compile(optimizer = adam1, loss = ‘mean_squared_error’)
Nu klargøres træningen og antallet af epochs indstilles. Her kommer ens computerkræfter i spil og jo bedre en computer man arbejder med, desto større kan antallet af epochs sættets op. Det her kommer til at tage noget tid, så lav en kop kaffe og sæt dig til rette.
num_epochs = 100
history=model.fit(features_set, labels, validation_split=0.1, epochs =num_epochs , batch_size = 32,shuffle=True)
Nu importeres out-of-sample dataen
VIX_testing_complete = pd.read_csv(‘forecast.csv’)
VIX_testing_processed = VIX_testing_complete.iloc[:, 4:5].values
VIX_total = pd.concat((VIX_training_complete[‘VIX Close’], VIX_testing_complete[‘VIX Close’]), axis=0)
For at forecaste en måned, skal jeg bruge de 60 tidligere obs. fra hver dag af VIX lukkekurs.
test_inputs = VIX_total[len(VIX_total) – len(VIX_testing_complete) – 60:].values
Dataen skaleres nu mellem -1 og 1
test_inputs = test_inputs.reshape(-1,1)
test_inputs = scaler.transform(test_inputs)
Testsættet indstilles nu på samme måde som træningssættet.
test_features = []
for i in range(60,88):
test_features.append(test_inputs[i-60:i, 0])
test_features = np.array(test_features)
test_features = np.reshape(test_features, (test_features.shape[0], test_features.shape[1], 1))
Nu kommer det sjove, for vi er nemlig klar til at forcaste lukkekursen for VIX, og dermed undersøge om det er muligt at tjene penge.
predictions = model.predict(test_features)
predictions = scaler.inverse_transform(predictions)
Vi har altså forecastet en måned frem, svarende til 25 bankdage, så lad og grafisk illustrere, hvor godt vores forecast passer på de sande værdier.
BOOOMMM! Det var da rimelig præcist? Vi skal nu undersøge om vi kan præcisere forecastet ved at indstille på netværkets opbygning. Man kan f.eks. indstille på hyperparameterne, hvis man valgt mulighed #2 fra tidligere.
plt.figure(figsize=(10,6))
plt.xticks(np.arange(1,num_epochs+1))
#plt.ylim(0.2,0.45)
plt.plot(np.arange(1,num_epochs+1),history.history[‘loss’])
plt.plot(np.arange(1,num_epochs+1),history.history[‘val_loss’])
plt.title(‘model loss’)
plt.ylabel(‘loss’)
plt.xlabel(‘epoch’)
plt.legend([‘train’, ‘test’], loc=’upper left’)
plt.show()
Her er illusteret nogle forskellige indstillinger, som gør det klart, hvilken opbygning er optimal.
For at undersøge, hvor robust LSTM netværket er undersøger vi lige om det var helt tilfældigt, at omkostningsfunktionen var så lav, eller om computeren rent faktisk fangede et system. Derfor varierer vi startdatoen for forecastet og undersøger om det er lige præcist.
Skriv et svar