In questo articolo descriviamo come effettuare la creazione di coppie di Ticker degli ETF da usare per il backtest di una strategia mean-reverting di trading algoritmico. L’articolo fa parte della miniserie “Backtest di una strategia di mean reverting con gli ETF”.
Gli articoli che fanno parte di questa serie sono:
- Backtest ETF: Web scraping e Database Sqlite3
- Backtest ETF: Creazione di coppie di Ticker
- Backtest ETF: Mean-reverting con Python
- Backtest ETF: Strategia di Trading con lo Z-score
- Backtest ETF: Pair trading con Python
Quindi articolo può essere considerato il primo vero articolo relativo allo script per il backtest di una strategia mean-revering sugli ETF che stiamo cercando di elaborare. Nel precedente articolo abbiamo creato il nostro database SQLite e lo abbiamo popolato con i dati degli ETF raccolti da www.etf.com. Abbiamo quindi a disposizione oltre 1000 ticker ETF da estrarre e utilizzare insieme a YFinance per scaricare i dati storici dei prezzi giornalieri dal Web e usarli nel backtest.
Tale approccio permette di avere un database con una serie di informazioni di supporto da abbinare a ciascuno ETF (come la classe dell’asset sottostante, la regione geografica e “focus”, solo per citarne alcuni) invece di avere solo un elenco di ticker ETF non identificabili. Queste informazioni consentonodi analizzare i ticker dell’ETF e creare coppie che hanno maggiori probabilità di co-integrarsi grazie a comuni fattori fondamentali sottostanti, invece che attraverso indicibili permutazioni di ticker casuali nella speranza di imbatterci in qualcosa di utile. Ad esempio, 2 ETF che tracciano l’argento come asset sottostante hanno maggiori probabilità di essere co-integrati rispetto a un ETF che traccia l’argento e un altro che traccia le società di servizi pubblici nella regione dell’Asia Pacifico, giusto? Sembra avere senso …
Iniziamo un po’ di codice!
Connessione al Database
import sqlite3 as db
import pandas as pd
# imposta il percorso del file del database a cui desideriamo connetterci
# questo è univoco e dipende dove si trova il database SQLite nel sistema locale
database = 'C:\Users\Datatrading\sqlite_databases\etfs.db'
# questa è l'istruzione SQL contenente le informazioni riguardo
# a quali ticker vogliamo estrarre dal database
# Ad esempio, ho scelto di estrarre tutti i ticker che hanno
# il loro "Focus" corrispondente a "Silver"
sql = 'SELECT Ticker FROM etftable WHERE Focus = "Silver";'
# crea una connessione al database
cnx = db.connect(database)
cur = cnx.cursor()
# eseque l'istruzione SQL e salva i risultati in una variabile chiamata "tickers"
tickers = pd.read_sql(sql, con=cnx)
Se stampiamo la varibiale “tickers” otteniamo il seguente DataFrame:
Ticker
0 SLV
1 SIVR
2 AGQ
3 USLV
4 ZSL
5 SLVO
6 DSLV
7 DBS
8 USV
9 SLV
10 SIVR
11 AGQ
12 USLV
13 ZSL
14 SLVO
15 DSLV
16 DBS
17 USV
Iteriamo questo DataFrame e aggiungiamo ogni ticker in una lista in modo da poterli usare più facilmente in seguito.
# crea una lista vuota
symbList = []
# iterazione sul DataFrame e inserimento dei ticker nella lista
for i in range(len(tickers)):
symbList.append(tickers.ix[i][0])
Creazione di Coppie di Ticker
Implementiamo quindi una funzione che prende la lista symbList
come input e restituisce una lista di coppie univoche di ticker. Lavoriamo con coppie di ticker perchè è l’input previsto dalla funzione principale di backtest, che implementiamo in seguito.
L’approccio usato in questo esempio può non essere quello più accurato per farlo, ma per il momento è il più semplice e fa bene il suo lavoro. Se tuttavia, alcuni esperti di python si imbattono in questo articolo e hanno qualche critica costruttiva, sono sempre felice di imparare nuove soluzioni.
def get_symb_pairs(symbList):
"""
symbList è una lista di simboli ETF
Questa funzione ha una lista di simboli come paramentro
e restituisce una lista di coppie univoche di simboli
"""
symbPairs = []
i = 0
# scorre la lista e crea tutte le possibili combinazioni di coppie
# di ticker e aggiunge le coppie alla lista "symbPairs"
while i < len(symbList) - 1:
j = i + 1
while j < len(symbList):
symbPairs.append([symbList[i], symbList[j]])
j += 1
i += 1
# scorre la lista di coppie appena creato e rimuove
# tutte le coppie composte da due ticker identici
for i in symbPairs:
if i[0] == i[1]:
symbPairs.remove(i)
# crea una nuova lista vuota per memorizzare solo coppie univoche
symbPairs2 = []
# scorre la lista originale e aggiunge alla nuova lista solo coppie univoche
for i in symbPairs:
if i not in symbPairs2:
symbPairs2.append(i)
return symbPairs2
Richiamo la funzione nella seguente istruzione:
symbPairs = get_symb_pairs(symbList)
Otteniamo il seguente elenco:
[[u'SLV', u'SIVR'],
[u'SLV', u'AGQ'],
[u'SLV', u'USLV'],
[u'SLV', u'ZSL'],
[u'SLV', u'SLVO'],
[u'SLV', u'DSLV'],
[u'SLV', u'DBS'],
[u'SLV', u'USV'],
[u'SIVR', u'AGQ'],
[u'SIVR', u'USLV'],
[u'SIVR', u'ZSL'],
[u'SIVR', u'SLVO'],
[u'SIVR', u'DSLV'],
[u'SIVR', u'DBS'],
[u'SIVR', u'USV'],
[u'SIVR', u'SLV'],
[u'AGQ', u'USLV'],
[u'AGQ', u'ZSL'],
[u'AGQ', u'SLVO'],
[u'AGQ', u'DSLV'],
[u'AGQ', u'DBS'],
[u'AGQ', u'USV'],
[u'AGQ', u'SLV'],
[u'AGQ', u'SIVR'],
[u'USLV', u'ZSL'],
[u'USLV', u'SLVO'],
[u'USLV', u'DSLV'],
[u'USLV', u'DBS'],
[u'USLV', u'USV'],
[u'USLV', u'SLV'],
[u'USLV', u'SIVR'],
[u'USLV', u'AGQ'],
[u'ZSL', u'SLVO'],
[u'ZSL', u'DSLV'],
[u'ZSL', u'DBS'],
[u'ZSL', u'USV'],
[u'ZSL', u'SLV'],
[u'ZSL', u'SIVR'],
[u'ZSL', u'AGQ'],
[u'ZSL', u'USLV'],
[u'SLVO', u'DSLV'],
[u'SLVO', u'DBS'],
[u'SLVO', u'USV'],
[u'SLVO', u'SLV'],
[u'SLVO', u'SIVR'],
[u'SLVO', u'AGQ'],
[u'SLVO', u'USLV'],
[u'SLVO', u'ZSL'],
[u'DSLV', u'DBS'],
[u'DSLV', u'USV'],
[u'DSLV', u'SLV'],
[u'DSLV', u'SIVR'],
[u'DSLV', u'AGQ'],
[u'DSLV', u'USLV'],
[u'DSLV', u'ZSL'],
[u'DSLV', u'SLVO'],
[u'DBS', u'USV'],
[u'DBS', u'SLV'],
[u'DBS', u'SIVR'],
[u'DBS', u'AGQ'],
[u'DBS', u'USLV'],
[u'DBS', u'ZSL'],
[u'DBS', u'SLVO'],
[u'DBS', u'DSLV'],
[u'USV', u'SLV'],
[u'USV', u'SIVR'],
[u'USV', u'AGQ'],
[u'USV', u'USLV'],
[u'USV', u'ZSL'],
[u'USV', u'SLVO'],
[u'USV', u'DSLV'],
[u'USV', u'DBS']]
Fantastico! Abbiamo un elenco completo di coppie univoche di ticker composte da ETF che hanno la parola “Silver” nel campo “Focus”.