Come descritto nel precedente articolo di questa serie, nel campo della finanza quantitativa in cui vi è un’abbondanza di dati di addestramento, quindi si potrebbe usare un modello come il Support Vector Machine (SVM). Tuttavia, gli SVM soffrono della mancanza di interpretabilità. Questo non è il caso degli Alberi Decisionali e i  Random Forest.

Questi ultimi sono spesso usati per preservare l’interpretabilità, qualcosa che i classificatori “black box” come SVM non forniscono. In definitiva, quando i dati sono molto numerosi (ad esempio i dati di tick), non è molto importante quale classificatore utilizzare. In questa fase sorgono altri fattori come l’efficienza computazionale e la scalabilità dell’algoritmo. La regola empirica prevede che il raddoppio dei dati di allenamento causa un aumento lineare delle prestazioni, ma poiché le dimensioni dei dati diventano considerevoli, questo miglioramento si riduce a un aumento sub-lineare delle prestazioni.

La teoria matematica e statistica alla base dei classificatori supervisionati è abbastanza complessa, ma l’intuizione di base di ciascun classificatore è semplice da comprendere. Inoltre, si noti che ognuna delle seguenti classificazioni avrà un diverso insieme di caratteristiche per le quali avranno migliori prestazioni, quindi se si riscontra un cattivo funzionamento del classificatore, ciò potrebbe essere dovuto al fatto che il set di dati utilizzato viola una di queste caratteristiche utilizzate.

 

Classificatore Naive Bayes

Nonostate questo classificatore non sia stato descritto nel precedente articolo, per completezza è opportuno introdurre anche questo tipo di classificatore. Il Naive Bayes (in particolare Multinomial Naive Bayes – MNB) è utile quando esiste un set di dati limitato. Questo perché è un classificatore ad alta distorsione. L’ipotesi principale del classificatore MNB è l’indipendenza condizionata. In sostanza, tale metodo non è in grado di discernere le interazioni tra le singole caratteristiche, a meno che non siano state specificatamente aggiunte come una caratteristica extra.

Ad esempio, si consideri uno scenario di classificazione dei documenti, che avviene nelle analisi finanziarie quando si tenta di eseguire l’analisi delle opinioni. L’MNB potrebbe apprendere che singole parole come “gatto” e “cane” potrebbero riferirsi rispettivamente ai documenti relativi a cani e gatti, ma la frase “gatti e cani” (gergo britannico che significa più o meno “piovere a catinelle”) non sarebbe considerata una caratteristica meteorologica dal classificatore! Una soluzione potrebbe essere considerare “gatti e cani” come una caratteristica extra, e quindi associarli a una categoria meteorologica.

 

Logistic Regression

La regressione logistica offre alcuni vantaggi rispetto al modello di Naive Bayes in quanto vi è meno attenzione sulla correlazione tra le caratteristiche e, per la natura del modello, c’è un’interpretazione probabilistica dei risultati. Questo è più adatto per un ambiente in cui è necessario utilizzare le soglie. Ad esempio, potremmo desiderare di posizionare una soglia dell’80% per un risultato “up” o “down” in modo che sia selezionata correttamente, anziché scegliere la categoria con la probabilità più elevata. In quest’ultimo caso, la previsione di “up” potrebbe essere del 51% e la previsione di “down” potrebbe essere del 49%. In questo caso impostare la categoria su “up” non è una previsione molto precisa.

 

Decision Tree and Random Forests
Gli alberi decisionali (DT) suddividono lo spazio in una gerarchia di scelte booleane che portano a una categorizzazione, o raggruppamento, in base alle rispettive decisioni. Questo li rende altamente interpretabili (assumendo un numero “ragionevole” di decisioni / nodi nell’albero!). DT ha molti vantaggi, inclusa la capacità di gestire le interazioni tra le caratteristiche, anche quelle non parametriche.
Sono anche utili nei casi in cui è difficile (o impossibile) separare linearmente i dati in classi (che è una condizione richiesta dai SVM). Lo svantaggio dell’uso di alberi decisionali individuali è che sono soggetti all’overfitting (alta varianza). Questo problema viene risolto utilizzando una Random Forest. Le foreste casuali sono in realtà alcuni dei classificatori “migliori” che sono utilizzati nelle applicazioni di machine learning, quindi devono essere sempre prese in considerazione.

 

Support Vector Machine
Support Vector Machines (SVM), pur avendo un complesso processo di allenamento, è in realtà relativamente semplice da comprendere. Gli SVM lineari tentano essenzialmente di partizionare uno spazio in gruppi distinti, utilizzando confini di separazione lineari. Per alcuni tipi di dati questo può funzionare molto bene e porta a buone previsioni. Tuttavia molti dati non sono linearmente separabili, quindi gli SVM lineari possono avere uno scarso rendimento.

La soluzione è modificare il kernel utilizzato dal SVM, in modo da permette l’uso di confini decisionali non lineari. Quindi sono modelli abbastanza flessibili. Tuttavia, il corretto confine del SVM deve essere scelto tra i migliori risultati. Gli SVM sono particolarmente adatti per i problemi di classificazione dei testi di gradi dimensioni. Hanno lo svantaggio di avere un’elevata complessità computazionale, dalla difficoltà di messa a punto e dal fatto che il modello allenato è difficile da interpretare.

Forecasting del Movimento di un Indice Azionario

L’indice S&P500 è un indice ponderato delle 500 maggiori società quotate in borsa (per capitalizzazione di mercato) nel mercato azionario statunitense. È spesso considerato un “benchmark” azionario. Esistono molti prodotti derivati ​​per consentire la speculazione su questo indice. In particolare, il contratto futures E-Mini S& P500 è uno strumento estremamente liquido per negoziare l’indice.

In questo esempio useremo un set di classificatori per prevedere la direzione del prezzo di chiusura al giorno k in base esclusivamente alle informazioni sui prezzi conosciute al giorno k-1. Un movimento direzionale verso l’alto significa che il prezzo di chiusura del giorno k è superiore al prezzo del giorno k-1, mentre un movimento al ribasso implica un prezzo di chiusura al giorno k è inferiore a quello del giorno  k-1.

Se siamo in grado di determinare la direzione del movimento con un metodo che superi significativamente un tasso di successo del 50%, con un basso errore e una buona significatività statistica, allora siamo sulla buona strada per creare una semplice strategia di trading sistematico, basata sulle nostre previsioni. In questa fase non ci occupiamo degli algoritmi di Machine Learning più aggiornati e di ultima generazione. In questo momento stiamo solo introducendo alcuni concetti e quindi iniziamo la discussione con alcuni metodi elementari.

Implementazione in Python

Per l’implementazione di questi forecaster si utilizza aclune librerie Python, quali NumPy, Pandas e Scikit-Learn, la cui installazione è stata descritta in un precedente articolo.

Il primo passo è importare i principali moduli e le librerie, in paticolare per questo esempio si deve importare i moduli per LogisticRegression, LDA, QDA, LinearSVC (una Support Vector Machine lineare), SVC (una Support Vector Machine non lineare) e i classificatori RandomForest:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# forecast.py

import datetime
import numpy as np
import pandas as pd
import sklearn

from pandas.io.data import DataReader
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.lda import LDA
from sklearn.metrics import confusion_matrix
from sklearn.qda import QDA
from sklearn.svm import LinearSVC, SVC

Dopo aver importato le librerie, si deve creare un DataFrame di pandas che contenga i i ritorni (variazioni di prezzo) percentuali ritardati per un numero precedente di giorni (predefinito a cinque). La funzione create_lagged_series crea un DataFrame per un determinato simbolo azionario (come restituito da Yahoo Finance) eil periodo specificato.

Il codice è ben commentato quindi dovrebbe essere semplice capire il suo funzionamento:

def create_lagged_series(symbol, start_date, end_date, lags=5):
"""
This creates a Pandas DataFrame that stores the percentage returns 
of the adjusted closing value of a stock obtained from Yahoo Finance, 
along with a number of lagged returns from the prior trading days
(lags defaults to 5 days). Trading volume, as well as the Direction 
from the previous day, are also included.
"""
# Obtain stock information from Yahoo Finance
ts = DataReader(
      symbol, "yahoo",
      start_date-datetime.timedelta(days=365),
      end_date
     )

# Create the new lagged DataFrame
tslag = pd.DataFrame(index=ts.index)
tslag["Today"] = ts["Adj Close"]
tslag["Volume"] = ts["Volume"]

# Create the shifted lag series of prior trading period close values
for i in range(0, lags):
    tslag["Lag%s" % str(i+1)] = ts["Adj Close"].shift(i+1)

# Create the returns DataFrame
tsret = pd.DataFrame(index=tslag.index)
tsret["Volume"] = tslag["Volume"]
tsret["Today"] = tslag["Today"].pct_change()*100.0

# If any of the values of percentage returns equal zero, set them to
# a small number (stops issues with QDA model in Scikit-Learn)
for i,x in enumerate(tsret["Today"]):
    if (abs(x) < 0.0001):
        tsret["Today"][i] = 0.0001

# Create the lagged percentage returns columns
for i in range(0, lags):
    tsret["Lag%s" % str(i+1)] = \
    tslag["Lag%s" % str(i+1)].pct_change()*100.0

# Create the "Direction" column (+1 or -1) indicating an up/down day
tsret["Direction"] = np.sign(tsret["Today"])
tsret = tsret[tsret.index >= start_date]

return tsret

Inseriamo la funzione di classificazione all’interno di una procedura principale. In questo esempio specifico si vuol prevedere la direzione del mercato azionario statunitense nel 2005, utilizzando i dati sui rendimenti dal 2001 al 2004.

Innanzitutto si crea una serie ritardata di S&P500 utilizzando cinque ritardi. La serie include anche il volume degli scambi. Tuttavia, si limita il set predittore da utilizzare a solo i primi due ritardi. Quindi stiamo implicitamente affermando al classificatore che gli ulteriori ritardi sono di minor valore predittivo. Per inciso, questo effetto è studiato più concretamente sotto il concetto statistico di autocorrelazione, che va oltre lo scopo di questo articolo.
Dopo aver creato l’array di predittori X e il vettore di risposta Y, possiamo suddividere gli array in un set di training e un set di test. Il primo sottoinsieme viene utilizzato per addestrare effettivamente il classificatore, mentre il secondo viene utilizzato per testare le prestazioni. Si dividono i dati al 1 ° gennaio 2005, utilizzando i dati precedenti per il training e i dati successivi per il testing.

Una volta creata la divisione tra training e testing, è necessario creare un’array dei modelli di classificazione, ognuno dei quali è una tupla che contiene un identificativo e il nome della funzione o modulo. In questo esempio non si imposta nessun parametro per la regressione logistica, gli analizzatori discriminatori lineari / quadratici o i modelli di classificazione SVM lineare, mentre si utilizza un set di parametri predefiniti per i SVM  radiali (RSVM) e il Random Forest (RF).
Dopo aver iterato i dati sui  modelli, si forma (fitting) ciascun modello sui dati di training e poi si effettuano previsioni sui dati di testing. Infine si calcola il Hit-Rate e la matrice di confusione per ciascun modello:

if __name__ == "__main__":
    # Create a lagged series of the S&P500 US stock market index
    snpret = create_lagged_series(
        "^GSPC", datetime.datetime(2001,1,10), 
        datetime.datetime(2005,12,31), lags=5
       )

# Use the prior two days of returns as predictor
# values, with direction as the response
X = snpret[["Lag1","Lag2"]]
y = snpret["Direction"]

# The test data is split into two parts: Before and after 1st Jan 2005.
start_test = datetime.datetime(2005,1,1)

# Create training and test sets
X_train = X[X.index < start_test] X_test = X[X.index >= start_test]
y_train = y[y.index < start_test] y_test = y[y.index >= start_test]

# Create the (parametrised) models
print("Hit Rates/Confusion Matrices:\n")
models = [("LR", LogisticRegression()),
          ("LDA", LDA()),
          ("QDA", QDA()),
          ("LSVC", LinearSVC()),
          ("RSVM", SVC(
             C=1000000.0, cache_size=200, class_weight=None,
             coef0=0.0, degree=3, gamma=0.0001, kernel=’rbf’,
             max_iter=-1, probability=False, random_state=None,
             shrinking=True, tol=0.001, verbose=False)
            ),
          ("RF", RandomForestClassifier(
             n_estimators=1000, criterion=’gini’,
             max_depth=None, min_samples_split=2,
             min_samples_leaf=1, max_features=’auto’,
             bootstrap=True, oob_score=False, n_jobs=1,
             random_state=None, verbose=0)
            )]
# Iterate through the models
for m in models:

    # Train each of the models on the training set
    m[1].fit(X_train, y_train)

    # Make an array of predictions on the test set
    pred = m[1].predict(X_test)

    # Output the hit-rate and the confusion matrix for each model
    print("%s:\n%0.3f" % (m[0], m[1].score(X_test, y_test)))
    print("%s\n" % confusion_matrix(pred, y_test))

Risultati

Di seguito si riporta l’output di tutti i modelli di classificazione. Probabilmente avrai  valori diversi per l’output del RF (Random Forest) dato che quest’ultimo è intrinsecamente stocastico per definizione:
Hit Rates/Confusion Matrices:

LR:
0.560
[[ 35  35]
 [ 76 106]]

LDA:
0.560
[[ 35  35]
 [ 76 106]]

QDA:
0.599
[[ 30  20]
 [ 81 121]]

LSVC:
0.560
[[ 35  35]
 [ 76 106]]

RSVM:
0.563
[[  9   8]
 [102 133]]

RF:
0.504
[[48 62]
 [63 79]]

Da notare che tutti i tassi di successo si collocano tra il 50% e il 60%. Quindi si può concludere che le variabili ritardate non sono indicative della direzione futura. Tuttavia, se si osserva all’analizzatore discriminante quadratico (QDA), si nota che la sua prestazione predittiva complessiva sul set di test è appena inferiore al 60%.
Dalla matrice di confusione per questo modello (e gli altri in generale) si osserva che il tasso positivo per i giorni “down” è molto più alto rispetto ai giorni “up”. Quindi, se vogliamo creare una strategia di trading basata su queste informazioni, potremmo considerare di limitare le operazioni alle sole posizioni short del S&P500 come metodo potenziale per aumentare la redditività.

Negli articoli successivi useremo questi modelli come base di una strategia di trading incorporandoli direttamente nella struttura di backtesting event-driven e utilizzando uno strumento diretto, come un exchange traded fund (ETF), per poter accedere al trading del S&P500.

Recommended Posts