Di recente mi sono imbattuto in un articolo che mostra come l’applicazione si tecniche di ML basate su LSTM possano generare risultati migliori rispetto a una strategia B&H. La prima osservazione è che il singolo titolo non fa testo, occorre avere un paniere sufficientemente ampio per dimostrare che un algoritmo di ML sia efficace.
Subito dopo ho voluto provare l’algoritmo, conscio dei risultati scadenti che ho sempre ottenuto tramite LSTM mi sono chiesto quali fossero gli elementi che rendono quest’implementazione LSTM così efficace, anche solo per un singolo caso. Soprattutto perché risultati così buoni li ho ottenuti solo con ‘lookahead bias’ ossia i dati che alimentano il calcolo contengono informazioni sullo stato futuro.
Questo ovviamente è possibile solo per errori o sviste di programmazione. Quindi mi sono messo a caccia della chiave vincente o dell’errore. Devo essere sincero il codice è di buona qualità e trovare la chiave di volta non è stato semplice. Le serie temporali sono sempre ostiche, basta uno shift sbagliato e salta tutto, in questo caso non c’è stato bisogno di uno shift per fare la magia, che si verifica nel punto in cui viene preparato il dataset di test e il risultato. Precisamente il passaggio magico (o incriminato che dir si voglia) è quello indicato con In [17]
scaler=MinMaxScaler(feature_range=(0,1))
dg=pd.DataFrame(scaler.fit_transform(dfm[["High","Low","Open","Close","Volume","fd_cm_open",\
"mv_avg_12","mv_avg_24","fd_nm_open"]].values))
dg0=dg[[0,1,2,3,4,5,6,7]]
window=4
dfw=create_window(dg0,window)
X_dfw=np.reshape(dfw.values,(dfw.shape[0],window+1,8))
print(X_dfw.shape)
print(dfw.iloc[:4,:])
print(X_dfw[0,:,:])
y_dfw=np.array(dg[8][window:])
Guardando nel dettaglio come vengono costruiti i vari campi scopriamo che i valori in ingresso per High, Low, Open, Close sono il valore medio del mese corrente, ossia dal primo giorno del mese all’ultimo giorno del mese e che il valore di cui si vuole fare la previsione è il primo valore del mese successivo. Va da se che la previsione è falsata dai valori medi del mese corrente.
L’errore è indotto dal fatto che l’algoritmo lavora sul primo giorno mese. Intuitivamente se a gennaio stimo il mese di febbraio, un conto è stimare il valore del primo giorno di febbraio, un conto è stimare il valore l’ultimo giorno di febbraio. Una correzione veloce consiste nel shiftare i due valore relativi all’apertura del mese corrente e del prossimo mese di -1. E’ quindi sufficiente inserire questo blocco di codice
#run once
dfm["fd_nm_open"] = dfm["fd_nm_open"].shift(-1)
dfm["fd_cm_open"] = dfm["fd_cm_open"].shift(-1)
dfm=dfm.dropna()
E quindi la verifica visiva che stiamo guardando i dati giusti
print(dfm[["High","Low","Open","Close","Volume","fd_cm_open", "mv_avg_12","mv_avg_24","fd_nm_open"]].head(1))
print(df.loc['1975-02'].mean())
print(df.loc['1975-02'].iloc[0].loc['Close'])
print(df.loc['1975-03'].iloc[0].loc['Close'])
Il risultato è il seguente:
High Low Open Close \
Date
1975-01-31 00:00:00-05:00 73.546817 71.442273 0.0 72.564091
Volume fd_cm_open mv_avg_12 mv_avg_24 \
Date
1975-01-31 00:00:00-05:00 1.966136e+07 77.82 82.84377 95.136477
fd_nm_open
Date
1975-01-31 00:00:00-05:00 83.029999
Open 0.000000e+00
High 8.098842e+01
Low 7.889211e+01
Close 8.009684e+01
Volume 2.229684e+07
dtype: float64
77.81999969482422
83.02999877929688
il campo fd_cm_open per il 31 gennaio 1975 contiene il valore di apertura del mese di febbraio, mentre il campo fd_nm_open contiene il valore di apertura del mese di marzo. In pratica posizionandoci il primo giorno di febbraio ci stiamo chiedendo se il primo marzo avremo guadagnato o perso. Per farlo utilizziamo la madia 12 e 24 calcolate sulla media mensile compreso il mese di Gennaio. Ora facciamo girare l’algoritmo con il solito fit e …. vediamo se il risultato rimane positivo.
Atteso il tempo per completare il training ecco a confronto i grafici prima e dopo.
Il risultato ottenuto adesso è in linea con le mie aspettative. Non ci sono dati sufficiente per un training che possa generare i risultati così positivi come quelli presentati nell’articolo.
Valgono sempre le considerazioni di base per tutti gli algoritmi che si trovano in giro, se fossero efficaci non verrebbero pubblicati ma sfruttati dal creatore e pubblicati sono quando hanno terminato la loro efficacia. Ossia, non è tutto oro ciò che lucica.