Introduzione alle Reti Neurali nel Trading
Negli articoli precedenti :
Le reti neurali si basano su un insieme di unità connesse (neuroni), che, proprio come le sinapsi in un cervello, possono trasmettere un segnale ad altri neuroni, in modo tale che, agendo come cellule cerebrali interconnesse, possono imparare e prendere decisioni in un modo più simile a quello umano.
Proprio per la loro complessità la teoria alla base di questo metodo previsionale é piuttosto ostica e poco intuitiva, ancora non del tutto comprensibile neanche ai piu’ addetti ai lavori. Cercheremo tuttavia di semplificare la parte introduttiva per tuffarci nel Deep Learning alla ricerche di nuove metodologie di supporto al trading. Questa volta testeremo gli algoritmi sull’andamento del GOLD.
Prerequisiti per il tutorial sulle Reti Neurali nel Trading
Per la trattazione dell’argomento si farà uso del software di programmazione Python in ambiente Jupiter Notebook.
Quando il totale é maggiore della somma dei suoi elementi
Sono quasi sicuro che tutti I lettori conoscano la scannerizzazione OCR (“optical character recognition”) di documenti che possano essere importati in files a formato word. Ebbene il riconoscimento di un carattere é una delle tante applicazioni di elaborazioni fatte con reti neurali. Immaginiamo di avere un prblema in cui dobbiamo far riconoscere (prevedere) al computer se quello che vediamo in Figura 1 (un nove scritto a mano libera) e’ effettivamente un “9”.
Possiamo immaginare che il numero “9” cosi scritto possa essere tradotto in una sequenza di pixel (un vettore) in cui il livello di intensità di bianco vari da un minimo di 0 (parte nera) con valore 0,5 (grigia ai bordi) fino ad un massimo di 1 (parte centrale del carattere in bianco) in ogni posizione dell’area in osservazione.
Quello che avremo è approssimativamente (e in bassa risoluzione) cio’ che vediamo in Figura 2.
Con ragionevole certezza possiamo dire che un altro “9” scritto da un altra persona abbia un vettore diverso ma con una simile distribuzione e relazione tra i vari pixel o tra gli element del vettore. Dove troviamo tanti pixel ad intensità di bianco uguale a 1 sappiamo che siamo nel bel mezzo della figura (o del numero nel nostro caso) che vogliamo rappresentare; se ad esempio vediamo che l’intensità dei pixel nella parte alta a destra diminuisce significa che siamo in una zona in cui il carattere ha una forma non rettilinea. Il signolo pixel non ci dice nulla ma la relazione con I pixel nel suo intorno ci aiuta a comprendere l’informazione in quell’intorno. Tanto piu’ piccolo e’ il singolo pixel tanto minore è l’informazione sulla totalità che possiamo ricavare da esso stesso, preso come singolo.
Al contrario pero’ guardando l’insieme, una figura composta da piccolo pixel ci risulterà piu’ comprensibile. Le reti neurali funzionano bene con una grande quantità di informazione. Il nostro cervello fa piu’ o meno la stessa cosa anche se non ce ne rendiamo conto. Possiamo immaginare che quando alimentiamo una rete neurale con quell numero “9” le diverse intensità di bianco vengono singolarmente computate e moltiplicate per certi fattori (vettore pesi) che genereranno (per il tramite di una funzione di attivazione 1 ) un nuovo vettore che viene confrontato con un vettore di un “9” standard.
Questo “9” standard é quello che, in un processo di “supervised” machine learning, chiamiamo etichetta (variabile dipendente o label). I fattori moltiplicativi verranno iterativamente ottimizzati e univocamente definiti quando il confronto numerico tra il vettore di input è sufficientemente bene replicato dal vettore etichetta. Chiamiamo input il vettore ad intensità di bianco del numero “9” scritto a mano Chiamiamo layer la serie di nodi in cui vengono fatte le operazioni di moltiplicazione delle intensità con un vettore pesi. Parliamo di Deep Leanring quando il numero di layer sono piu’ di uno.
Chiamamo output, il vettore che rappresenta l’etichetta, con cui addestriamo la rete neurale affinche il vettore pesi venga ottimizzato in modo tale che le differenze tra input e output siano minime. Tale fase di ottimizzazione iterative (detta anche backpropagation) è quello che avviene nella fase di traiing della rete neurale
La rete neurale pertanto trova le migliori relazioni tra I pixel (il pattern) in modo tale che ad un certo inpiut sia associate l’output con buona approssimazione. Dopo aver “educato” la rete neurale ed aver trovato il miglior vettore pesi, possiamo provare ad inserire un nuovo “9” scritto a mano ( da un altra persona) e lanciare la funzione “previsione” per vedere se essa da effettivamente “9” Quando vogliamo adattare la rete neurale al trading, ovviamente gli inputs sono I dati storici. Ogni singolo dato puo’ essere accomunato ad un pixel, ogni signolo dato e’ parte di un pattern, che viene determinate dall’ottimizzazione di un vettore pesi che rende il dato di chiusura giornaliero sempre coerente (con certa apprssimazione) ai valori di apertura, massimo e minimo di giornata, in relazione al giorno precendente e al giorno successivo etc…
Algoritmi per Reti Neurali e Trading
Ci sono due librerie principali per costruire reti neurali: TensorFlow (sviluppato da Google) e PyTorch (sviluppato da Facebook). Per questo tutorial, userò TensorFlow e una sua libreria di alto livello di supporto front-end (interfaccia utente) chiamata Keras, Un potente tipo di rete neurale progettato per gestire la dipendenza dalla sequenza è chiamato reti neurali ricorrenti.
La rete Long Short-Term Memory o rete LSTM2 è un tipo di rete neurale ricorrente usata nell'apprendimento profondo, ovvero architetture di dati molto grandi che proprio per questo possono essere addestrate con successo. Sono usate in quasi tutte le applicazioni di analisi delle serie temporali (come le previsioni del tempo, le macchie solari, ecc.) e permettono di evitare la dipendenza dalle informazioni passate di lungo termine. Come se guardando un video ci si ricordasse solo della scena precedente, in un libro solo cio’ che è stato letto nel capitolo precedente. Allo stesso modo le RNN (Recurrent Neural Network) ricordano le informazioni precedenti e le usano per elaborare l'input corrente.
LSTM su GOLD
Seguendo il solito principio di costruzione di un codice di machine learning3 , partiamo con l’importazione delle librerie di gestione tabelle e grafica
import pandas_datareader.data as web
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
STEP 1 – IMPORTAZIONE E LETTURA DATI
Procediamo con l’impostazione delle funzioni che ci permettono di leggere I valori della quotazione dell’oro che vogliamo analizzare :
pip install yfinance
data = yf.download(tickers='GC=F', period='5y', interval='1d')
STEP 2 – ELABORAZIONE, TRASFORMAZIONE E ORGANIZZAZIONE DATI
In questa fase trasformiamo I dati in maniera tale che gli stessi possano essere gestiti dall’algoritmo LSTM che utilizzeremo. Per esempio, eliminiamo le colonne che non ci servono, sostituiamo i valori nulli dei weekend e dei giorni di festa con i valori dell’ultimo giorno precedente in cui il mercato era aperto
cols_to_keep = ["Close"]
data = data.loc[:,cols_to_keep]
closingdata = data closing
data["week"] = closingdata.index.isocalendar().week
idx = pd.date_range(closingdata.index[0], closingdata.index[-1])
completedata =closingdata.reindex(idx, fill_value= float("NaN"))
completedata = completedata.fillna(method='ffill')
cols_to_keep = ["Close"]
alldata = completedata.loc[:,cols_to_keep]
alldata
L’ultima riga di codice ci darà la possibilità d visualizzare la nuova struttura dati con due sole colonne (F3):
STEP 3 – DEFINIZIONE DEL MODELLO
Importiamo la libreria che ci permetterà di implementare LSTM ed eseguire le operazioni per esso necessarie con le seguenti istruzioni
import keras
from keras.models import Sequential
from tensorflow.keras.layers import LSTM,Dense,Dropout,Activation, Flatten, Conv2D, MaxPooling2D
from sklearn.preprocessing import MinMaxScaler
Prima di adattare i dati al nostro modello, doveva essere preelaborato perché il modello LSTM funziona in modo efficiente con valori scalati dei dati (tra 0 e 1). Per fare questo utilizziamo la funzione MinMaxScaler della libreria sklearn.
preprocessing dataset = alldata.values
scaler = MinMaxScaler(feature_range=(0,1))
scaled_dataset = scaler.fit_transform(dataset)
STEP 4 – ADDESTRAMENTO DEL MODELLO
Per l’addestramento del modello dobbiamo innanzitutto definire quelli che chiamiamo Data Test Set e Data Train Set, tra loro complementari, rispettivamente variabili dipendenti e variabili indipendenti, ricavati dalla separazione della totalità dei dati ottenuti alla fine dello Step 1. Il set di dati è stato quindi suddiviso in training e set di test con l'80 % dei dati per l'addestramento e il 20 % per i test
training_data_len=math.ceil(len(dataset)*.8)
data_trainingscaled = scaled_dataset[:training_data_len]
data_testingscaled = scaled_dataset[training_data_len:]
e costruiamo la serie di dati in modo da essere elaborate dal modello LSTM con 100 giorni di look back
x_trainscaled = [] y_trainscaled = []
for i in range(100, data_trainingscaled.shape[0]):
x_trainscaled.append(data_trainingscaled[i-100 : i])
y_trainscaled.append(data_trainingscaled[i,0])
x_trainscaled,y_trainscaled = np.array(x_trainscaled), np.array(y_trainscaled)
Procediamo adesso con la creazione della rete neurale con 2 strati LSTM e Dropouts uno strato Dense e uno strato di output finale
model = Sequential()
model.add(LSTM(units=50,activation='relu',return_sequences=True ,input_shape=(x_trainscaled.shape[1],1)))
model.add(Dropout(0.2)) model.add(LSTM(units=50,activation='relu',return_sequences=False )) model.add(Dropout(0.2))
model.add(Dense(25))
model.add(Dense(units=1))
Tutti I parametri come il modello di rete neurale (sequential), dimensione e numero di layer sono modificalbili, come pure la funzione di attivazione che in questo caso è una ReLu (rectifier linear unit). Procediamo con il suo addestramento:
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x_trainscaled,y_trainscaled, batch_size=32,epochs=50)
Il modello è stato addestrato per 50 iterazioni (parametro epochs, anche questo variabile)
STEP 5 – VALUTAZIONE AFFIDABILITA’ DEL MODELLO
Ora andiamo a far girare le line di codice che ci permettono di capire la bontà del modello andando a generare una “previsione”, sulla base del subset di dati scelto per il testing. In realtà questa “previsione” è finta, visto che la stiamo facendo su dati passati, che sono il nostro elemento di confronto per la valutazione della bontà del modello. Per fare questo dobbiamo transformare il set di dati in maniera tale da essere elaborato dalla rete neurale (sugli ultimi 100 giorni) con la seguente serie di istruzioni:
x_testscaled = [] y_testscaled = []
for i in range(100, data_testingscaled.shape[0]):
x_testscaled.append(data_testingscaled[i-100 : i])
y_testscaled.append(data_testingscaled[i,0])
x_testscaled , y_testscaled = np.array(x_testscaled) ,np.array(y_testscaled)
y_testscaledreshaped = np.reshape(y_testscaled,(y_testscaled.shape[0],1))
y_test = scaler.inverse_transform(y_testscaledreshaped)
A questo punto lanciamo la previsione : testpredictionscaled = model.predict(x_testscaled) ed operiamo l’operazione descaling per riportare I valori ai livelli reali dai valori scalati tra 0 e 1.
testprediction=scaler.inverse_transform(testpredictionscaled)
Manipolando addesso la serie di dati otteniamo il dataframe di Figura 4
Che elaborato in forma grafica restituisce il confronto fino al 02.02.2022 in Figura 5.
STEP 6 - PREVISIONE
Avendo costruito il modello ed avendo appurato la sua precisione5 , se accettabile, procediamo con la previsione nei giorni futuri. Per la previsione futura al modello vengono forniti i dati degli ultimi 100 giorni (valore di riferimento del LSTM) che vengono quindi utilizzati per prevedere il trend nei sette giorni successivi.
x_input=data_testingscaled[265:].reshape(1,-1)
x_input.shape temp_input=list(x_input)
temp_input=temp_input[0].tolist()
lst_output=[] n_steps=100 i=0
while(i<7):
if(len(temp_input)>100):
#print(temp_input)
x_input=np.array(temp_input[1:])
print("{} day input {}".format(i,x_input))
x_input=x_input.reshape(1,-1)
x_input = x_input.reshape((1, n_steps, 1))
#print(x_input)
yhat = model.predict(x_input, verbose=0)
print("{} day output {}".format(i,yhat))
temp_input.extend(yhat[0].tolist())
temp_input=temp_input[1:]
#print(temp_input)
lst_output.extend(yhat.tolist())
i=i+1
else:
x_input = x_input.reshape((1, n_steps,1))
yhat = model.predict(x_input, verbose=0)
print(yhat[0])
temp_input.extend(yhat[0].tolist())
print(len(temp_input))
lst_output.extend(yhat.tolist())
i=i+1
print(lst_output)
E con alcune trasformazioni, determiniamo il vettore previsionale (Figura 6).
Che con le funzioni di plotting ci disegnail grafico Figura 7
e il suo zooming sulla previsione Figura 8
Conclusioni sulle Reti Neurali nel Trading
Questo approccio alla previsione dall’andamento dell’ Orofornisce una stima relativamente buona del prezzo del giorno di negoziazione successivo. Questa stima varia a seconda della volatilità del prezzo delle azioni e del valore di lookback, dato che diversi valori di lookback danno diverse previsioni di prezzo.
Il modello può essere combinato con altre caratteristiche, come indicatori tecnici, medie mobili, volumi, modelli di candele, per fare previsioni ancora più consolidate e perchè no, anche con altri modelli di analisi di serie temporali (ARIMA, AUTOARIMA e ARCH) Tuttavia le reti neurali sono estremamente sensibili ai parametri di inputs (dimensione dati iniziali, dati di training e testing, dimensione rete neurale, numero di iterazioni, funzione di attivazione) ma è altrettanto affascinante lavorare su questi e percorrere le fasi di addestramento fino al raggiungimento della sua stabilità.
Le dinamiche nelle rete neurali mi fanno pensare all’enorme lavoro di squadra che c’e’ in un alveare di uno sciame di api,(o in un formicaio) dove la casualità e la caoticità del lavoro dei singoli perde significato rispetto all’ordine generato al livello superiore della loro collettività. Ecco allora che ritroviamo l’idea per cui il totale risulta superiore all’insieme dei suoi componenti 6 , principio ispiratore del concetto di intelligenza collettiva.
Si intende con questo la capacità di un gruppo di agire in modo che il risultato delle loro azioni sembri essere intelligente o in qualche modo rappresentativo di un piano consapevole. Un risultato che risulti avere un senso, un qualcosa che inglobi una qualsiasi forma di valore aggiunto, che ha un livello di elaborazione ragionevolmente superiore al risultato raggiungibile dal singolo, e che soddisfi tutti gli elementi del gruppo.
Lo vediamo nelle sessioni di brainstorming. Tanto maggiore é la capacità di trasmettere informazione tra I vari elementi del gruppo tanto piu’ le decisioni sono consapevoli, la loro collaborazione piu’ profiqua, il loro effetto piu’ potente……….nel bene o nel male. Le reti neurali fanno un qualcosa di simile, per il momento e per fortuna ancora inconsapevolmente.
Interessante, no?
Per chi volesse ricevere il codice completo e strutturato per un plug and play in ambiente Jupiter Notebook, richiedetelo pure all’autore all’indirizzo email nicola.matarese73@gmail.com
Nicola Matarese classe 1973, ha conseguito una laurea magistrale in ingegneria meccanica ed ha ricoperto ruoli manageriali all’interno di realtà industriali in ambito aerospazio e difesa.
E’ appassionato di trading e opera sui mercati dal 2001. Da qualche anno lavora a sistemi di trading supportati da teorie sui Big Data e Intelligenza Artificiale.
Comments