Lo scopo di questo progetto è quello di analizzare diversi algoritmi per le previsioni finanziarie di alcune azioni molto famose. Questa analisi è stata condotta da due aspiranti Data Scientist, pertanto non costituisce e non è da considerarsi una consulenza finanziaria.

Esistono molti modelli statistici per i mercati finanziari. Il comportamento di questi modelli è simile a quello dei modelli dei fenomeni naturali. Cioè, entrambi sono influenzati da variabili sconosciute e instabili. Ciò comporta un’elevata e imprevedibile volatilità. È quindi quasi impossibile prevedere il comportamento futuro.

** Per l’analisi delle serie storiche abbiamo consultato questo PDF del Prof. Vito Ricci: –> https://cran.r-project.org/doc/contrib/Ricci-ts-italian.pdf **

Per il trattamento delle classi (ts, xts, zoo) abbiamo consultato questo PDF di Rotman School University of Toronto –> https://tdmdal.github.io/r-tutorial-202021-winter/r_timeseries_finance_pkgs.pdf

Librerie usate:

require(xts)
require(doParallel)
require(caret)
require(dynlm)
library(zoo)
library(nnet)
library(MASS)
library(Rsolnp)
library(nlme)
library(TTR)
library(depmixS4)
library(ggplot2)
library(reshape2)
library(PerformanceAnalytics)
library(quantmod)
library(forecast)
library(highcharter)
library(tseries)
library(timeSeries)
library(TSstudio)
library(dplyr)
library(ggthemes)
require(kableExtra)
library(dynlm)
library(tidyr)
library(TSA)
library(gridExtra)
library(corrplot)
library(plotly)
library(GGally)
library(data.table)
library(lubridate)
library(DT)
library(e1071)
library(doParallel)
library(knitr)
library(ggdark)
library(ggplot2)
library(depmixS4)
# library(tensorflow)
# library(keras)
# options(kableExtra.latex.load_packages = FALSE)
# ##### NOTA BENE (SE HAI UN MAC CON CPU SILICON GLI ULTIMI AGIONAMENTI DI TENSOR E KERAS NON FUNZIONANO)--> Ho dovuto eseguire il downgrade di tensorflow-macos alla versione 2.9.0 e tensorflow-metal alla 0.5.0 per eliminare tali errori. Altri su questo sito hanno segnalato problemi simili con versioni più recenti. Apple deve ancora fornire versioni funzionanti per le nuove versioni di TF.
# #https://developer.apple.com/forums/thread/722361

Utilizziamo la libreria “quantmod” per scaricare gli indici azionari interessati

price_apple <- getSymbols("AAPL", auto.assign=FALSE, from="2018-01-01", to="2022-12-31")
price_google <- getSymbols("GOOGL",auto.assign=FALSE,from="2018-01-01",to="2022-12-31")
price_amazon <- getSymbols("AMZN",auto.assign=FALSE,from="2018-01-01",to="2022-12-31")
price_tesla <- getSymbols("TSLA",auto.assign=FALSE,from="2018-01-01",to="2022-12-31")

Con la libreria “highcharter” visualizziamo gli indici azionari interessati:

highchart(type="stock") %>% 
  hc_add_series(Cl(price_apple), name="AAPL") %>% 
  hc_add_series(Cl(price_google), name="GOOGL") %>% 
  hc_add_series(Cl(price_amazon), name="AMZN") %>% 
  hc_add_series(Cl(price_tesla), name="TSLA") %>% 
  hc_title(text="<b>AAPL vs GOOGL vs AMZN Closing Price</b>")

Da questo primo grafico vediamo che il prezzo più alto c’è l’ha Apple

Calcoliamo i rendimenti azionari

return_apple <- dailyReturn(Cl(price_apple))
return_google <- dailyReturn(Cl(price_google))
return_amazon <- dailyReturn(Cl(price_amazon))
return_tesla <- dailyReturn(Cl(price_tesla))

Creiamo una nuova variabile (returns) che conterrà tutti i rendimenti azionari

returns <- data.frame(return_apple,return_google,return_amazon,return_tesla)
names(returns) <- c("return_apple","return_google","return_amazon","return_tesla")
returns <- as.xts(returns)
charts.PerformanceSummary(returns,main="Rendimenti giornalieri Apple vs Google vs Amazon vs Tesla 2018-2022")

TSLA presenta un un trend Ribassista nei mesi di Aprile e di settembre 2022 e APPL presenta un andamento rialzista a medio lungo termine, tuttavia si sono rivelati avere il più alto rendimento cumulativo rispetto a gli altri. Nella parte di drawdown possiamo vedere che ad oggi APPL è posizionata al disopra di tutte le altre. Questo significa che ha avuto un calo solo a marzo 2020 (come tutte le altre) e fin da allora non è mai più scesa a differenza delle altre

Nella parte di drawdown possiamo vedere che ad oggi APPL è posizionata al disopra di tutte le altre. Questo significa che ha avuto un calo solo a marzo 2020 (come tutte le altre) e fin da allora non è mai più scesa a differenza delle altre.

se vuoi sapere di più sul drawdown puoi cliccare qui –>https://www.avatrade.it/education/market-terms/what-is-drawdown

Dopo aver fatto una breve analisi, sugli indici azionari scelti, abbiamo deciso di continuare ad approfondire la nostra analisi basandoci solo l’indice AAPL

chartSeries(price_apple, theme="black",
            TA="addVo();addBBands();addCCI()", subset = '2018-01::')

** Questo grafico ci mostra l’andamento dell’indice apple con le bande di bollinger, il volume e il CCI (Commodity Channel Index) se vuoi ulteriori informazioni sulle bande di bollinger e il CCI puoi visitare questi link: https://www.avatrade.it/education/technical-analysis-indicators-strategies/cci-trading-strategies https://www.ig.com/it/strategie-di-trading/cosa-sono-le-bande-di-bollinger-e-come-usarle-nel-trading-190122**

highchart(type="stock") %>% 
  hc_add_series(price_apple) %>% 
  hc_add_series(SMA(na.omit(Cl(price_apple)),n=50),name="SMA(50)") %>% 
  hc_add_series(SMA(na.omit(Cl(price_apple)),n=200),name="SMA(200)") %>% 
  hc_title(text="<b>APPLE Price Candle Stick Chart 2018-2022</b>") %>% 
  hc_add_theme(hc_theme_darkunica())

Tali visualizzazioni possono essere molto utili quando è necessario vedere più dettagliatamente i dati. Le Medie mobili singole (SMA) sono anche più facili da vedere con un grafico interattivo. rispetto ai grafici statici. Questo grafico può essere utilizzato per evidenziare importanti analisi tecniche che influenzano il nostro processo decisionale. come la “croce d’oro” o la “croce della morte”. Se vuoi saperne di più su quest’ultima puoi dare un occhiata a questo sito > https://www.mazzieroresearch.com/golden-cross-che-cose/

“ts_decompose” è una funzione della libreria TS_studio, ci permette di vedere quattro grafici interattivi sui prezzi di chiusura

closing_pr <- Cl(to.monthly(price_apple))
dc_ts <- ts_decompose(as.ts(closing_pr, start=c(2018)))
dc_ts
dc <- decompose(as.ts(closing_pr, start=c(2018,1,1)))
dc$seasonal
##             Jan        Feb        Mar        Apr        May        Jun
## 2018  5.7966447 -3.3755472 -2.8190606 -3.4712485 -9.5817688 -7.1869758
## 2019  5.7966447 -3.3755472 -2.8190606 -3.4712485 -9.5817688 -7.1869758
## 2020  5.7966447 -3.3755472 -2.8190606 -3.4712485 -9.5817688 -7.1869758
## 2021  5.7966447 -3.3755472 -2.8190606 -3.4712485 -9.5817688 -7.1869758
## 2022  5.7966447 -3.3755472 -2.8190606 -3.4712485 -9.5817688 -7.1869758
##             Jul        Aug        Sep        Oct        Nov        Dec
## 2018  1.8963295  8.5080738  0.8137514 -0.3231225  2.4619044  7.2810195
## 2019  1.8963295  8.5080738  0.8137514 -0.3231225  2.4619044  7.2810195
## 2020  1.8963295  8.5080738  0.8137514 -0.3231225  2.4619044  7.2810195
## 2021  1.8963295  8.5080738  0.8137514 -0.3231225  2.4619044  7.2810195
## 2022  1.8963295  8.5080738  0.8137514 -0.3231225  2.4619044  7.2810195

L’output ci mostra quattro grafici dei nostri dati sui prezzi di chiusura, che sono:

Observeded <- ci spiega l’andamento dell’indice azionario

Trend <- con il trend possiamo vedere la significativa tendenza al rialzo, iniziata intorno alla metà dell’anno 2019.

**Seasonal <- fluttuazione stagionale ripetitiva dei dati. Il prezzo di chiusura di APPL tende a raggiungere il più alto ad Agosto (Generalmente nel mese di settembre vengono presentati nuovi dispositivi come iphone e computer, questo potrebbe spiegare l’andamento rialzista) e il più basso a Maggio. Guardando questo modello, possiamo dire che nel complesso, il momento giusto per vendere questo titolo era nel mese di Dicembre, Gennaio ma soprattutto Agosto e il momento giusto per acquistare era nel mese di Maggio e Giugno, questo lo possiamo vedere meglio eseguendo questa riga di codice —> dc$seasonal.**

**Random <- fluttuazione irregolare o casuale non catturata dal trend e dalla stagionalità, L’attuale situazione della pandemia di Covid-19 è un esempio del fatto che potrebbe causare questa fluttuazione casuale.**

seas <- ts(price_apple, start=c(2018), end=c(2022, 12), frequency=12)

seas
##          AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
## Jan 2018   42.5400   43.0750  42.3150    43.0650   102223600      40.95049
## Feb 2018   43.1325   43.6375  42.9900    43.0575   118071600      40.94336
## Mar 2018   43.1350   43.3675  43.0200    43.2575    89738400      41.13354
## Apr 2018   43.3600   43.8425  43.2625    43.7500    94640000      41.60185
## May 2018   43.5875   43.9025  43.4825    43.5875    82271200      41.44733
## Jun 2018   43.6375   43.7650  43.3525    43.5825    86336000      41.44259
## Jul 2018   43.2900   43.5750  43.2500    43.5725    95839600      41.43308
## Aug 2018   43.6475   43.8725  43.6225    43.8200    74670800      41.66843
## Sep 2018   44.0450   44.3400  43.9125    44.2725   101672400      42.09871
## Oct 2018   44.4750   44.8475  44.0350    44.0475   118263600      41.88477
## Nov 2018   44.0375   44.8125  43.7675    44.7750   137547200      42.57653
## Dec 2018   44.8425   45.0250  44.5625    44.8150   124773600      42.61456
## Jan 2019   44.6525   44.8950  44.3525    44.6150   129700400      42.42439
## Feb 2019   44.3250   44.4450  44.1500    44.2500   108434400      42.07732
## Mar 2019   44.3250   44.8600  44.2050    44.2600   130756400      42.08682
## Apr 2019   44.3125   44.3250  43.3000    43.5550   204420400      41.41644
## May 2019   43.6275   43.7375  42.6325    42.7775   166116000      40.67711
## Jun 2019   43.0000   43.0000  42.5150    42.8775   156572000      40.77220
## Jul 2019   42.5400   42.5400  41.7675    41.9900   202561600      39.92828
## Aug 2019   41.3825   41.8425  41.1750    41.7425   184192800      39.69294
## Sep 2019   41.7175   42.1100  41.6250    41.8575   129915600      39.80228
## Oct 2019   41.7925   42.1550  41.6900    41.9450   188923200      39.88549
## Nov 2019   41.5000   41.7000  40.0250    40.1250   346375200      38.15486
## Dec 2019   39.7750   40.9700  39.0000    39.1225   290954000      37.20157
## Jan 2020   38.7075   40.9300  38.5000    40.7575   272975200      38.75629
## Feb 2020   40.7725   40.8500  39.7675    39.8850   206434400      37.92663
## Mar 2020   40.0725   40.2500  38.7575    38.7875   217562000      36.88302
## Apr 2020   39.2675   39.4725  37.5600    39.1025   282690400      37.33416
## May 2020   39.6250   40.9725  39.3775    40.6775   243278000      38.83792
## Jun 2020   40.4875   41.1875  40.4125    41.0850   130196800      39.22700
## Jul 2020   40.7600   41.8850  40.7200    41.8425   162579600      39.95025
## Aug 2020   42.4475   43.2725  42.2500    43.2475   204588800      41.29171
## Sep 2020   43.0900   43.7050  42.9425    43.1075   160704400      41.15802
## Oct 2020   43.0125   43.5650  42.8550    42.9625   135722000      41.01959
## Nov 2020   43.2075   43.5300  42.7525    42.7675   149886400      40.83342
## Dec 2020   42.9500   43.4875  42.9275    43.1250   123967600      41.17475
## Jan 2021   43.4175   43.9125  43.3850    43.8750   135249600      41.89082
## Feb 2021   44.0875   44.8475  44.0525    44.7425   152648800      42.71910
## Mar 2021   44.7750   45.1200  44.5400    44.5975   155712400      42.58064
## Apr 2021   44.8150   45.1550  44.5125    44.5300   151128400      42.51620
## May 2021   44.6350   44.9450  43.1650    43.7500   195208000      41.77147
## Jun 2021   43.2000   44.0750  43.1125    44.0525   153816000      42.06031
## Jul 2021   43.8025   44.4350  43.6300    44.2050   113605600      42.20590
## Aug 2021   44.4775   44.5625  44.0325    44.1675    95154000      42.17009
## Sep 2021   43.7350   43.9625  43.5675    43.7575   126814000      41.77864
## Oct 2021   43.8700   44.2800  43.7675    44.2350    95096400      42.23455
## Nov 2021   44.4900   45.0000  44.3475    44.9950   128740800      42.96017
## Dec 2021   45.0725   45.5975  45.0525    45.4300   128828400      43.37550
## Jan 2022   45.6475   45.8750  44.8100    44.9925   126774000      42.95779
## Feb 2022   45.0800   45.1300  44.4525    44.6100   117473600      42.59260
## Mar 2022   44.6250   45.0600  44.5175    44.6625    90975200      42.64271
## Apr 2022   44.6625   44.7800  44.4050    44.5050   157618800      42.49233
## May 2022   44.3300   44.3675  43.4150    43.8250   133787200      41.84309
## Jun 2022   43.8100   44.2000  43.7350    43.8100    78597600      41.82877
## Jul 2022   43.7600   43.7725  42.8150    42.8175   148219600      40.88115
## Aug 2022   42.5000   43.1700  42.1500    42.2125   165963200      40.30351
## Sep 2022   42.0975   42.4800  41.2350    41.2350   164115200      39.37021
## Oct 2022   42.0175   43.2750  41.6100    43.1925   150164800      41.23919
## Nov 2022   43.4200   43.7875  41.7300    42.0850   163690400      40.18178
## Dec 2022   41.8125   42.5050  41.2975    41.6200   166674000      39.73780
## attr(,"src")
## [1] yahoo
## attr(,"updated")
## [1] 2023-01-08 15:00:42 CET
## attr(,"index")
##  [1] 1514851200 1514937600 1515024000 1515110400 1515369600 1515456000
##  [7] 1515542400 1515628800 1515715200 1516060800 1516147200 1516233600
## [13] 1516320000 1516579200 1516665600 1516752000 1516838400 1516924800
## [19] 1517184000 1517270400 1517356800 1517443200 1517529600 1517788800
## [25] 1517875200 1517961600 1518048000 1518134400 1518393600 1518480000
## [31] 1518566400 1518652800 1518739200 1519084800 1519171200 1519257600
## [37] 1519344000 1519603200 1519689600 1519776000 1519862400 1519948800
## [43] 1520208000 1520294400 1520380800 1520467200 1520553600 1520812800
## [49] 1520899200 1520985600 1521072000 1521158400 1521417600 1521504000
## [55] 1521590400 1521676800 1521763200 1522022400 1522108800 1522195200
## attr(,"index")attr(,"tzone")
## [1] UTC
## attr(,"index")attr(,"tclass")
## [1] Date
ts_seasonal(seas, type = "all") 
## Warning in ts_seasonal(seas, type = "all"): The input object is a 'mts' class,
## by defualt will use only the first series as an input

ts_seasonal è anche una funzione della libreria “TS_studio”, in questo caso mettendo type=all , possiamo vedere dal grafico la stagionalità per anni e per mesi, ci consente di identificare i modelli stagionali

Modelli markoviani **Il Markov nascosto è modellato da un insieme predeterminato di gaussiane. Il problema del rilevamento del dominio è un problema di apprendimento non supervisionato, poiché il numero di stati non è noto a priori e non esiste una “verità di base” per “addestrare” l’HMM.

Questa sezione risolve due problemi di modellazione distinti: il primo consiste nell’adattare un HMM con due stati di modalità ai rendimenti dell’indice AAPL Il secondo consiste nell’utilizzare tre stati; Abbiamo usato la libreria “DepmixS4”**

L’oggetto della serie temporale applerest può essere tracciato, mostrando i periodi volatili intorno al 2018 e al 2023

plot(appleRest)

Un modello di Markov nascosto a due stati viene montato utilizzando l’algoritmo EM “Expectation-maximization”

breve guida –> https://rstudio-pubs-static.s3.amazonaws.com/154174_78c021bc71ab42f8add0b2966938a3b8.html

#Adattiamo un modello di Markov nascosto con due stati:
hmm <- depmix(returns ~ 1, family = gaussian(), nstates = 2, data=data.frame(returns=returns))
hmmfit <- fit(hmm, verbose = FALSE)
## converged at iteration 52 with logLik: 3213.266
post_probs <- posterior(hmmfit)
## Warning in .local(object, ...): Argument 'type' not specified and will default
## to 'viterbi'. This default may change in future releases of depmixS4. Please
## see ?posterior for alternative options.
plot(returns, type='l', main='Regime Detection', xlab='', ylab='Returns')

matplot(post_probs[,-1], type='l', main='Regime Posterior Probabilities', ylab='Probability')
legend(x='bottomleft', c('Regime #1','Regime #2'), fill=1:2, bty='n')

Traccia il flusso di returns e il regime posteriore probabilità dei regimi separati In questo caso abbiamo un HMM con 2 stati nascosti, questo grafico ci mostra due curve di probabilità, una per ogni stato nascosto. L’asse x del grafico rappresenta il tempo e l’asse y rappresenta la probabilità di trovarci in un particolare stato nascosto. l’altezza della curva ad ogni passo temporale, indica la probabilità di essere in quello stato in quel momento e la larghezza la rappresenta la durata dello stato

HMM con 3 stati

plot(returns, type='l', main='Regime Detection', xlab='', ylab='Returns')

matplot(post_probs1[,-1], type='l', main='Regime Posterior Probabilities', ylab='Probability')
legend(x='bottomleft', c('Regime #1','Regime #2', 'Regime #3'), fill=1:3, bty='n')

In questo caso abbiamo 3 curve, come detto precedentemente ognuna delle quali rappresenta la probabilità di essere in uno dei 3 stati.

Navigando su Medium abbiamo Letto un articolo interessante che tratta Hidden Markov Models for Time Series in R studio [Stock Market Data] scritto da Ankit Sekseria, abbiamo provato a replicarlo, e adattare il suo modello ai nostri dati. **link* https://medium.com/analytics-vidhya/hidden-markov-models-for-time-series-in-r-studio-5ae2b9fb0701

Estraiamo informazioni relative all’indice AAPL, il set di dati contiene informazioni relative al prezzo di apertura al 01-01-2018 e chiusura al 02-01-2023 delle azioni, al prezzo alto e basso e al volume delle azioni. Siamo interessati a modellare con il nostro HMM la differenza tra il valore di chiusura e il valore di apertura del giorno corrente.

mod1 <- depmix(AAPL.Close ~ 1, family = gaussian(), nstates = 5,
               data = AAPL_train)
set.seed(1)
fm2 <- fit(mod1, verbose = FALSE) 
probs <- posterior(fm2)
## Warning in .local(object, ...): Argument 'type' not specified and will default
## to 'viterbi'. This default may change in future releases of depmixS4. Please
## see ?posterior for alternative options.
head(probs)
##   state         S1        S2           S3            S4           S5
## 1     2 0.00000000 1.0000000 0.000000e+00  0.000000e+00 0.000000e+00
## 2     2 0.03625842 0.9637416 0.000000e+00 1.304355e-110 3.309715e-55
## 3     2 0.03339146 0.9665772 4.250485e-58  3.136680e-05 1.804330e-38
## 4     2 0.03975022 0.9602045 3.038961e-41  3.398249e-05 1.129905e-05
## 5     2 0.03440663 0.9655479 1.485854e-08  3.680699e-05 8.606687e-06
## 6     2 0.03566405 0.9642929 1.169993e-08  3.355836e-05 9.447186e-06
AAPL_predict <- cbind(AAPL_subset$AAPL.Close, probs$state)

head(AAPL_predict)
##            AAPL.Close probs.state
## 2018-01-02    43.0650           2
## 2018-01-03    43.0575           2
## 2018-01-04    43.2575           2
## 2018-01-05    43.7500           2
## 2018-01-08    43.5875           2
## 2018-01-09    43.5825           2
chartSeries(AAPL_predict [,1])

addTA(AAPL_predict[AAPL_predict [,2]==1,1],on=1, type= "p", col=5,pch=25)

addTA(AAPL_predict[AAPL_predict [,2]==2,1],on=1, type= "p", col=6,pch=24)

addTA(AAPL_predict[AAPL_predict [,2]==3,1],on=1, type= "p", col=7,pch=23)

addTA(AAPL_predict[AAPL_predict [,2]==4,1],on=1, type= "p", col=8,pch=22)

addTA(AAPL_predict[AAPL_predict [,2]==5,1],on=1, type= "p", col=10,pch=21)

Arima Forecasting

** L’idea alla base delle previsioni delle serie temporali è quella di utilizzare i dati passati e attuali per prevedere il futuro. Anche se non tutti i modelli utilizzati per le previsioni possono essere accurati al 100%, i risultati sono comunque generalmente molto utili per prendere decisioni sul futuro. Esistono molti modelli e algoritmi per la previsione dei dati delle serie temporali.

Come analisti, dobbiamo decidere quale rapporto di separazione ci permetterà di presentare i dati di addestramento e di test senza uneccessivo costocomputazionale perl’addestramento del modello. Il rapporto più comune è 80% train - 20% test, ma dipende dai dati e dallo scopo dellaprevisione. *splitting:

*train data: dati utilizzati per adattarsi al modello

*test data: dati utilizzati per valutare il modello

In questo caso vogliamo prevedere i prezzi dei titoli su 100 giorni nel set di dati. Utilizzeremo quindi le ultime 100 osservazioni come dati di prova e il resto dei dati come dati di addestramento.***

# Numero di giorni che vogliamo prevedere
n <- 100

Effettuiamo lo split del dataset dividendo i dati in train e test:

# Splitting the data
train <- head(Cl(price_apple), length(Cl(price_apple))-n)
test <- tail(Cl(price_apple), n)

Il metodo ingenuo (Naive Bayes) è un metodo di previsione in cui l’ultima osservazione viene utilizzata come risultato previsto dei dati. Viene utilizzato come modello di base per le previsioni. Se i risultati del nostro modello sono peggiori di quelli del metodo ingenuo, decidiamo di non utilizzare questo modello.

fc_na <- naive(train, h=n)

plot dei risultati:

autoplot(fc_na) +
  autolayer(ts(test, start=length(train)), series = "forecast")

La linea blu è la media della nostra previsione, mentre le aree più scure e chiare più scure, che rappresentano rispettivamente gli intervalli di confidenza dell’80% e del 95%. Se confrontiamo il risultato con i dati di prova effettivi, possiamo vedere che ci sono differenze tra loro.

** Arima Model **

**Il modello autoregressivo integrato a media mobile (ARIMA) è una combinazione di un modello autoregressivo, un modello di integrazione per differenza e un modello a media mobile. Il modello autoregressivo (AR) determina la relazione tra un’osservazione e diverse osservazioni ritardate; per utilizzare il modello ARIMA i dati delle serie temporali devono essere stazionari, il che può essere ottenuto differenziando i dati. Infine, il modello di media mobile (MA) definisce la relazione tra l’errore residuo del modello di media mobile rispetto alle osservazioni ritardate e i valori osservati. I modelli ARIMA sono solitamente indicati come ARIMA(p,d,q), dove p, d e q sono parametri stimati positivamente; l’ordine AR (p) è il numero di osservazioni ritardate nel modello. Per d, d è il numero di volte in cui i dati effettivi vengono differenziati fino allo stato stazionario. In genere, non ci sono più di due differenziali prima di raggiungere lo stato stazionario; l’ordine MA (q) è la dimensione della finestra della media mobile. Esistono modi per determinare valori appropriati per questi parametri nel nostro modello, ma è difficile. Fortunatamente, R ha una funzione auto.arima() che fa questo lavoro per noi. Utilizzando questa funzione, possiamo ottenere due tipi di ARIMA: ARIMA non stagionale (che abbiamo appena descritto) e ARIMA stagionale. Ovviamente, l’ARIMA non stagionale non include la parte stagionale dei dati, mentre l’ARIMA stagionale sì. Vediamo quale fornisce i migliori risultati di previsione per i nostri dati.

**ARIMA non stagionale**
model_non <- auto.arima(train, seasonal=FALSE)
fc_non <- forecast(model_non, h=n)

plot dei risultati con Arima non stagionale

autoplot(fc_non)+
  autolayer(ts(test, start= length(train)), series="forecast")

I risultati ottenuti utilizzando l’ARIMA non stagionale hanno mostrato una tendenza al rialzo. Tuttavia, ci sono ancora alcune differenze tra i due quando si confrontano con i dati dei test reali.

** Arima stagionale**
model_s <- auto.arima(train)
fc_s <- forecast(model_s, h=n)
autoplot(fc_s)+
  autolayer(ts(test, start= length(train)), series="forecast")

I risultati mostrano che auto.arima() dà gli stessi risultati indipendentemente dal fatto che la parte stagionale dei dati sia inclusa o meno. Ciò significa che la parte stagionale dei dati non è significativa e che l’ARIMA non stagionale è il miglior modello ARIMA basato su auto.arima() per il nostro set di dati.

 **Forecast Evaluation - valutazione delle previsioni**
 

Dopo aver previsto i dati, l’ultima fase consiste nel valutare i risultati delle previsioni. La valutazione delle previsioni viene effettuata controllando che i residui siano coerenti con le ipotesi sui residui e confrontando gli indicatori di accuratezza. Controllo dei residui: Il residuo è la differenza tra i dati previsti e quelli effettivi. Un buon modello è quello in cui i residui sono distribuiti in modo casuale e non c’è un modello evidente. Questa sezione descrive le ipotesi residue che devono essere soddisfatte. La distribuzione è normale (media = 0) e può essere convalidata da una curva normale. La curva normale deve essere a campana. Ha una varianza costante, che può essere confermata da un grafico dei residui. La varianza costante è indicata da fluttuazioni costanti nei dati. Non c’è autocorrelazione, il che può essere confermato dal grafico ACF e dal test di Ljung-Box. L’autocorrelazione può essere rilevata sul grafico ACF se c’è una linea che supera il limite superiore o inferiore. Nel test di Ljung-Box il valore p deve essere maggiore di 0,05 per soddisfare questa ipotesi; se i risultati del grafico ACF e del test di Ljung-Box differiscono, si raccomanda di utilizzare i risultati del test di Ljung-Box.

residual of naive method

checkresiduals(fc_na)

## 
##  Ljung-Box test
## 
## data:  Residuals from Naive method
## Q* = 28.123, df = 10, p-value = 0.001725
## 
## Model df: 0.   Total lags used: 10

residual of arima model

checkresiduals(fc_non)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,1) with drift
## Q* = 19.183, df = 9, p-value = 0.02368
## 
## Model df: 1.   Total lags used: 10

I risultati mostrano che il metodo ingenuo non soddisfa l’ipotesi di assenza di autocorrelazione, mentre l’ARIMA soddisfa tutte le ipotesi. Pertanto, sulla base del controllo dei residui, il nostro modello ARIMA ha fornito risultati migliori rispetto al metodo ingenuo.

Confronto tra gli indicatori di accuratezza

Esistono diversi indicatori di accuratezza predittiva. In questa analisi verrà confrontato l’errore quadratico medio (RMSE) di entrambi i modelli. RMSE (Root Mean Squared Error, errore quadratico medio): deviazione standard dei residui che indica la loro distribuzione; un valore RMSE minore indica un risultato migliore.

Accuracy Metrics of Naive method

accuracy(fc_na) 
##                     ME     RMSE      MAE        MPE     MAPE MASE        ACF1
## Training set 0.1052288 2.084777 1.387875 0.09408151 1.469982    1 -0.06389016

** MPE

Accuracy Metrics of ARIMA

accuracy(fc_non)
##                        ME    RMSE      MAE         MPE     MAPE      MASE
## Training set 3.988944e-05 2.07685 1.379672 -0.05328961 1.461125 0.9940889
##                      ACF1
## Training set 0.0008176214

Forecasting con LSTM Memoria a breve termine a lungo termine LSTM può elaborare sia singoli dati che una sequenza, come ad esempio un video completo. Questa applicazione è per il riconoscimento vocale e il riconoscimento della scrittura a mano. Aiuta ad evitare problemi legati alla dipendenza a lungo termine. Il loro uso più comune è lo sviluppo del processo di apprendimento di enormi problemi. Anche la memoria a lungo e breve termine è una rete neurale ricorrente, ma è diversa dalle altre reti. Altre reti ripetono il modulo ogni volta che l’input riceve nuove informazioni. Tuttavia, LSTM ricorderà il problema per un tempo più lungo e ha una struttura a catena per ripetere il modulo. Esse interagiscono in un metodo speciale e contengono quattro strati di rete neurale. Se vuoi saperne di più trovi il link qui –> https://datascience.eu/it/apprendimento-automatico/comprensione-delle-reti-lstm/

price_apple_2018 <- getSymbols("AAPL", auto.assign=FALSE, from="2018-01-01", to="2022-12-31")
APPLE_log_returns <- price_apple_2018 %>% Ad() %>% dailyReturn(type = "log")

Dal momento che stiamo per fare previsione con un modello LSTM Per prevedere il prezzo dei giorni futuri, creiamo nuove variabili da aggiungere al nostro dataset, cosi da avere più informazioni e fare un buon allenamento

se vuoi sapere di più sul significato di queste variabili aggiuntive puoi visitare questo sito–>https://juliahub.com/ui/Packages/Indicators/0sN6c/0.7.0 oppure visitare il sito tel pacchetto usato “TTR” –> https://cran.r-project.org/web/packages/TTR/TTR.pdf

#Calcoliamo le medie mobili a 10-20-60 giorni
require(TTR)
price_apple_2018$Avg_volume_10 <- SMA(price_apple_2018$AAPL.Volume, n = 10)
price_apple_2018$Avg_volume_20 <- SMA(price_apple_2018$AAPL.Volume, n = 20)
price_apple_2018$Avg_volume_60 <- SMA(price_apple_2018$AAPL.Volume, n = 60)

# Calcoliamo la % del volume medio dei giorni sopra indicati
price_apple_2018$Volume_perc_avg_10 <- (price_apple_2018$AAPL.Volume/price_apple_2018$Avg_volume_10) * 100
price_apple_2018$Volume_perc_avg_20 <- (price_apple_2018$AAPL.Volume/price_apple_2018$Avg_volume_20) * 100
price_apple_2018$Volume_perc_avg_60 <- (price_apple_2018$AAPL.Volume/price_apple_2018$Avg_volume_60) * 100

# Calcoliamo l'intervallo tra massimo e minimo
price_apple_2018$Range <- price_apple_2018$AAPL.High - price_apple_2018$AAPL.Low



#ATTENZIONE ---- se avete problemi con questa funzione potrebbe esere per via del pacchetto dplyr... provate ad usare il pacchetto stats di R ( QUINDI USARE stats::lag INVECE DI log)

price_apple_2018$perc_change_closing <- (price_apple_2018$AAPL.Close - stats::lag(price_apple_2018$AAPL.Close))/stats::lag(price_apple_2018$AAPL.Close) * 100 # 

# Intervallo tra il prezzo di chiusura dei giorni precedenti e il prezzo di chiusura di oggi
price_apple_2018$change_from_yest <- price_apple_2018$AAPL.Close - stats::lag(price_apple_2018$AAPL.Close) # anche qui stesso problema di conflitto con dplyr (aggiungere stats:: )

# Ora calcoliamo di nuovo le varie medie mobili (MA) per il range 
# negli ultimi 10, 20 , 60 giorni
price_apple_2018$moving_avg_10 <- SMA(price_apple_2018$Range, n = 10)
price_apple_2018$moving_avg_20 <- SMA(price_apple_2018$Range, n = 20)
price_apple_2018$moving_avg_60 <- SMA(price_apple_2018$Range, n = 60)

# Calcoliamo la % dell'intervallo medio dei giorni sopra indicati
price_apple_2018$perc_moving_avg_10 <- (price_apple_2018$Range/price_apple_2018$moving_avg_10) * 100
price_apple_2018$perc_moving_avg_20 <- (price_apple_2018$Range/price_apple_2018$moving_avg_20) * 100
price_apple_2018$perc_moving_avg_60 <- (price_apple_2018$Range/price_apple_2018$moving_avg_60) * 100

# L'importo totale di denaro scambiato moltiplicato per il volume (in dollari)
price_apple_2018$cash_tradet <- price_apple_2018$AAPL.Close * price_apple_2018$AAPL.Volume

# Il volume medio di contante scambiato per gli stessi periodi di cui sopra
price_apple_2018$avg_cash_trated_10 <- SMA(price_apple_2018$cash_tradet, n = 10)
price_apple_2018$avg_cash_trated_20 <- SMA(price_apple_2018$cash_tradet, n = 20)
price_apple_2018$avg_cash_trated_60 <- SMA(price_apple_2018$cash_tradet, n = 60)

# La % del volume medio ad oggi.
price_apple_2018$Avg_Dollar_volume_pct_10 <- (price_apple_2018$cash_tradet/price_apple_2018$avg_cash_trated_10) * 100
price_apple_2018$Avg_Dollar_volume_pct_20 <- (price_apple_2018$cash_tradet/price_apple_2018$avg_cash_trated_20) * 100
price_apple_2018$Avg_Dollar_volume_pct_60 <- (price_apple_2018$cash_tradet/price_apple_2018$avg_cash_trated_60) * 100

# apertura - chiusura
price_apple_2018$nightgap <- price_apple_2018$AAPL.Open - stats::lag(price_apple_2018$AAPL.Close)

# Il Gap % di vincita o perdita rispetto ai prezzi di chiusura di ieri
price_apple_2018$night_gap_perc <- (price_apple_2018$AAPL.Open - stats::lag(price_apple_2018$AAPL.Close))/ stats::lag(price_apple_2018$AAPL.Close) * 100
price_apple_2018$perc_range_previous = abs((price_apple_2018$AAPL.Close - price_apple_2018$AAPL.Open)/(price_apple_2018$AAPL.High - price_apple_2018$AAPL.Low) * 100)
price_apple_2018$perc_range_atpr = (price_apple_2018$Range/price_apple_2018$AAPL.Close) * 100
price_apple_2018$perc_range_williams = (price_apple_2018$AAPL.High - price_apple_2018$AAPL.Close)/(price_apple_2018$AAPL.High - price_apple_2018$AAPL.Low) * 100
# Intervallo di calcolo per 1 mese

un_mese_range_perc <- rollapply(price_apple_2018$AAPL.High, 20, max) - rollapply(price_apple_2018$AAPL.Low, 20, max)
price_apple_2018$un_mese_range_perc = (price_apple_2018$AAPL.Close - price_apple_2018$AAPL.Low)/un_mese_range_perc * 100

#Le medie mobili uniformano i dati sui prezzi per formare un indicatore di trend following.

price_apple_2018$EMA10 <- EMA(price_apple_2018$AAPL.Low, n = 10)
price_apple_2018$EMA20 <- EMA(price_apple_2018$AAPL.Low, n = 20)
# Media mobile ponderata
price_apple_2018$EMA60 <- EMA(price_apple_2018$AAPL.Low, n = 60)
#La doppia media mobile esponenziale è una misura dell'andamento di un titolo
# media
price_apple_2018$WMA10 <- WMA(price_apple_2018$AAPL.Low, n = 10)
# L'EVWMA utilizza il volume per dichiarare il periodo dell'MA.
price_apple_2018$EVWMA10 <- EVWMA(price_apple_2018$AAPL.Low, price_apple_2018$AAPL.Volume)
# Zero Lag Exponential Moving Average (ZLEMA) Come nel caso del doppio
price_apple_2018$ZLEMA10 <- ZLEMA(price_apple_2018$AAPL.Low, n = 10)
# Prezzo medio ponderato per il volume (VWAP-"Volume-weighted average price") e media ponderata per il volume mobile
# prezzo
price_apple_2018$VWAP10 <- VWAP(price_apple_2018$AAPL.Low, price_apple_2018$AAPL.Volume)
# La media mobile di Hull (HMA), sviluppata da Alan Hull, è estremamente
# veloce
price_apple_2018$HMA10 <- HMA(price_apple_2018$AAPL.Low, n = 20)
# La media mobile ALMA utilizza la curva della distribuzione Normale (Gauss).
# quale
price_apple_2018$ALMA10 <- ALMA(price_apple_2018$AAPL.Low, n = 9, offset = 0.85, sigma = 6)

# salviamo il file in csv 
write.csv(price_apple_2018, file = "price_apple_2018_Federico_Guzzo.csv", row.names = F)

Test di Dickey-Fuller

adf.test(price_apple_2018$AAPL.Adjusted)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  price_apple_2018$AAPL.Adjusted
## Dickey-Fuller = -1.5744, Lag order = 10, p-value = 0.7585
## alternative hypothesis: stationary

Un modo per testare se una serie temporale è stazionaria o meno, è eseguire un test Dickey-Fuller, una serie temporale stazionaria è quella la cui media, varianza e autocorrelazione sono tutte costanti nel tempo. Al contrario, una serie temporale non è stazionaria quando queste tre statistiche cambiano nel tempo. Nel nostro caso abbiamo come risultato il p-value=0.7585, quindi NON possiamo, rifiutare l’ipotesi nulla perché il valore p non è inferiore a 0,05, questo indica che la nostra serie temporale non è stazionaria.

# Rimuoviamo le variabili altamente correlate per evitare l'overfitting dei modelli
del <- cor(AAPL_lm)
del[upper.tri(del)] <- 0
diag(del) <- 0
AAPL_lm <- AAPL_lm[, !apply(del, 2, function(x) any(x > 0.9))]

** Creiamo Train e test dal nostro dataset, vogliamo provare a fare una previsione temporale di 7 giorni tenendo in considerazione i valori predetti da quelli reali**

giorni_prev = 7
n = giorni_prev + 1
X_train = AAPL_lm[1:(nrow(AAPL_lm) - (n - 1)), -17]
# la nostra variabile dipendete è AAPL.Adjusted
y_train = AAPL_lm[n:nrow(AAPL_lm), 17]
X_test = AAPL_lm[((nrow(AAPL_lm) - (n - 2)):nrow(AAPL_lm)), -17]


require(quantmod)
# Creiamo il test di validazione dei prezzi reali dei prossimi 7 giorni 
AAPL2 = getSymbols("AAPL", from = "2022-12-21", to = "2023-01-01", auto.assign = FALSE)
nostre_date <- time(AAPL2)
y_test <- as.numeric(AAPL2$AAPL.Adjusted)

train <- cbind(X_train, y_train)
# check the number of features
dim(X_train)
## [1] 1193   16
dim(X_test)
## [1]  7 16
nostre_date
## [1] "2022-12-21" "2022-12-22" "2022-12-23" "2022-12-27" "2022-12-28"
## [6] "2022-12-29" "2022-12-30"

#KERAS Deep Learning: Backend TensorFlow Applica una rete di deep learning da strati strettamente accoppiati di uno stack lineare.

gc()
##           used  (Mb) gc trigger  (Mb) limit (Mb) max used  (Mb)
## Ncells 3583271 191.4    6235594 333.1         NA  6235594 333.1
## Vcells 6815723  52.0   12259901  93.6      16384 12259901  93.6
library(tensorflow)
## 
## Attaching package: 'tensorflow'
## The following object is masked from 'package:caret':
## 
##     train
library(keras)
## 
## Attaching package: 'keras'
## The following object is masked from 'package:depmixS4':
## 
##     fit
# ##### NOTA BENE (SE HAI UN MAC CON CPU SILICON GLI ULTIMI AGIONAMENTI DI TENSOR E KERAS NON FUNZIONANO)--> Ho dovuto eseguire il downgrade di tensorflow-macos alla versione 2.9.0 e tensorflow-metal alla 0.5.0 per eliminare tali errori. Altri su questo sito hanno segnalato problemi simili con versioni più recenti. Apple deve ancora fornire versioni funzionanti per le nuove versioni di TF.
# #https://developer.apple.com/forums/thread/722361
ker = ncol(X_train)

keras_model <- keras::keras_model_sequential()
keras_model %>% 
  layer_dense(units = 60, activation = 'relu', input_shape = ker) %>% 
  layer_dropout(rate = 0.2) %>% #We apply dropout  to prevent overfitting
  layer_dense(units = 50, activation = 'relu') %>%
  layer_dropout(rate = 0.2) %>%
  layer_dense(units = 1, activation = 'linear')

keras_model %>% compile(optimizer = "rmsprop", loss = "mse", metrics = "mse")

keras_history <- keras_model %>% fit(X_train, y_train, epochs = 100, batch_size = 28, 
                                     validation_split = 0.1, callbacks = callback_tensorboard("logs/run_a"))

link al video dell’allenamento del nostro modello https://www.youtube.com/watch?v=pmUZUtPUQYc

keras_pred <- keras_model %>% predict(X_test, batch_size = 28)
real_VS_pred <- data.frame(keras_pred, y_test)
ok <- as.data.frame(nostre_date)
df <- cbind(ok,real_VS_pred)
plot(keras_history)

gc()
##           used  (Mb) gc trigger  (Mb) limit (Mb) max used  (Mb)
## Ncells 3780082 201.9    6235594 333.1         NA  6235594 333.1
## Vcells 7185229  54.9   14791881 112.9      16384 12259901  93.6
real_VS_pred <- data.frame(keras_pred, y_test)

colnames(df) <- c("DATE","KERAS PRED", "REAL PRICES")
# 
p4 <- ggplot(real_VS_pred, aes(nostre_date)) + 
  geom_line(aes(y = keras_pred, colour = "keras_pred")) +
  geom_line(aes(y = y_test, colour = "real_prices")) +
  geom_point(aes(y = keras_pred,colour = "keras_pred"),size = 2) + 
  geom_point(aes(y = y_test, colour = "real_prices"),size = 2) +
  labs(title = "Keras (Predicted vs Actual)", x = "Date", y = "Daimler Share Price in $") +
  theme_solarized(light = FALSE)+
  dark_theme_gray() 
## Inverted geom defaults of fill and color/colour.
## To change them back, use invert_geom_defaults().
p4

gc()
##           used  (Mb) gc trigger  (Mb) limit (Mb) max used  (Mb)
## Ncells 3842144 205.2    6235594 333.1         NA  6235594 333.1
## Vcells 7286576  55.6   14791881 112.9      16384 12259901  93.6
require(kableExtra)
kable(df) %>% kable_material_dark(bootstrap_options = "bordered", full_width = F, 
                                      position = "center") %>% column_spec(1, bold = T, color = "red")
DATE KERAS PRED REAL PRICES
2022-12-21 135.2805 135.45
2022-12-22 132.0124 132.23
2022-12-23 137.5526 131.86
2022-12-27 133.9008 130.03
2022-12-28 131.3170 126.04
2022-12-29 136.2456 129.61
2022-12-30 136.1113 129.93

CONCLUSIONI

I dati delle serie temporali sono ovunque e la maggior parte di essi può influenzare la nostra decisione. La scomposizione dei dati delle serie temporali può darci una visione più dettagliata del modello dei nostri dati. Analizzarlo ci aiuterà a prendere decisioni sui nostri dati. Tuttavia, la decomposizione in R può essere fatta solo all’oggetto ts dove la frequenza verrà specificata. Dopo aver analizzato le nostre time-series, di solito vogliamo prevederli per aiutarci a prendere decisioni sul futuro. Tuttavia, bisogna tener sempre presente che il risultato delle previsioni non può mai essere accurato al 100%. La previsione può essere fatta usando molti modelli e algoritmi. In questa analisi, abbiamo usato il metodo NAIVE, il modello ARIMA, HMM, LSTM. Nella nostra analisi Il modello migliore risulta esser LSTM, in quanto è quello che si avvicina di più ai dati reali, sfortunatamente, il nostro modello non è ancora sufficiente per prevedere con successo le serie temporali del mercato.