Valutazione della qualità dei dati di Tiingo

Valutazione della qualità dei dati di Tiingo

Sommario Tutorial

TUTORIAL

In questo articolo descriviamo come effettuare la valutazione della qualità dei dati di Tiingo, un fornitore di dati e strumenti per il mercato azionario. Fondata nel 2014, Tiingo mira a potenziare i propri utenti fornendo dati validi, puliti e più accurati. Offrono dati OHLCV per 82.468 titoli globali, 37.319 azioni statunitensi e cinesi, 45.149 ETF e fondi comuni di investimento. Hanno anche un feed intraday grazie alla partnership con IEX. Inoltre, dispongono di dati fondamentali per le azioni statunitensi, ADR e cinesi e continuano a crescere. Tiingo dispone di dati provenienti da oltre 40 exchange di criptovalute con oltre 2100 ticker, oltre 40 ticker FX direttamente da banche di livello 1 e dark pool FX inclusi i dati top of book (bid/ask).

I dati di Tiingo

Quando ci iscriviamo per  usare la loro API, Tiingo offre oltre 30 anni di dati azionari per gli utenti gratuiti e premium e 5 anni di dati fondamentali per gli utenti gratuiti o oltre 15 anni su tutti i ticker per gli utenti premium. Tiingo utilizza 3 diverse borse valori per fornire i propri dati azionari che incorporano in un framework proprietario di pulizia dei dati per consentire il controllo degli errori e la ridondanza. Ciò aiuta a evitare dati mancanti e punti dati errati. I dati vengono aggiornati alle 17:30 EST per azioni/ETF e alle 00:00 EST per i NAV dei fondi comuni. Gli aggiornamenti in tempo reale sono disponibili direttamente da IEX Exchange. Le correzioni del cambio vengono applicate per tutta la sera fino alla chiusura del cambio alle 20:00.

Il piano gratuito di Tiingo non include l’accesso all’API dei dati fondamentali o all’API Tiingo News e presenta le seguenti limitazioni API:

  • 500 simboli unici al mese
  • 50 richieste all’ora
  • 1000 richieste al giorno

Il livello Power User costa solo $10 al mese e presenta le seguenti limitazioni API:

  • Accesso a tutti i simboli ogni mese
  • 5000 richieste all’ora
  • 50000 richieste al giorno

Il livello power user ha anche accesso all’API delle notizie e ai dati fondamentali come componente aggiuntivo. Al momento della stesura di questo articolo questa API era in versione beta con DOW30 disponibile per la valutazione. Entrambi i livelli sono solo per uso interno e personale. Offrono anche una gamma di licenze commerciali e pacchetti di ridistribuzione dei dati attraverso il contatto con il proprio team di vendita.

Obiettivo

Per effettuare la valutazione della qualità dei dati di Tiingo usiamo la REST API EOD di Tiingo per accedere ai dati di fine giornata per dieci azioni statunitensi. Vediamo come possiamo valutare visivamente la qualità dei dati tramite il metodo imshow() di Matplotlib. Modifichiamo quindi l’immagine per includere heatmap di terze parti e vediamo come aggiungere e posizionare le etichette degli assi. 

Questo approccio prevede di convertire i dati JSON in un DataFrame Pandas e quindi utilizzare il metodo df.mask() per creare il  grafico della figura con imshow(). Questo metodo può essere usato per qualsiasi serie di ticker, qualsiasi attributo di prezzo e qualsiasi intervallo di date. Il codice proposto è specifico per la risposta fornita dall’API EOD di Tiingo  ma può essere facilmente modificato in modo da usare altri fornitori di dati.

Questo articolo fa parte di una serie di tutorial del Trading Algoritmico. Se desideri seguire il tutorial, ti consigliamo di configurare un ambiente virtuale che utilizza la distribuzione Python Anaconda. Se desideri eseguire il codice senza seguire la serie o non hai installato Anaconda, avrai bisogno delle seguenti  librerie di Python:

  • Python >=v3.8
  • ipykernel v6.4
  • jupyter v1.0
  • Matplotlib v3.5
  • Numpy >=v1.22
  • Palettable v3.3
  • Pandas v1.4
  • Pandas Market Calendars v3.4
  • Requests v2.27

Connessione alla REST API di Tiingo

Dobbiamo registrarci a Tiingo per ottenere una chiave API. La chiave può essere recuperata nelle informazioni del profilo e d entrando nel menu API all’interno della sezione Token. Una richiesta di dati all’API di Tiingo fornisce una risposta JSON o CSV, dove JSON è l’impostazione  di default. Vediamo come convertire i dati da JSON a Pandas DataFrame. In  questa pagina Tiingo offre documentazione dettagliata sulla connessione all’API.

Per effettuare la valutazione della qualità dei dati di Tiingo in un DataFrame abbiamo bisogno di importare le seguenti librerie.

				
					import json
import pandas as pd
import requests
				
			
La prima cosa da fare è testare la connessione e assicurarci che la chiave API funzioni correttamente. Dopo aver importato le librerie richieste creiamo un dizionario chiamato headers. Questo contiene tutte le informazioni necessarie per connettersi all’API e ottenere la risposta JSON. Non dimentichiamoci di sostituire il valore della chiave ‘Authorizarion’ (attualmente ‘your_api_key’) con la chiave API  assegnata da Tiingo.
				
					
headers={
    'Content-Type': 'application/json',
    'Authorization': 'Token ' + 'your_api_key'
  }
				
			

Possiamo quindi creare la nostra prima richiesta API.

				
					
request = requests.get("https://api.tiingo.com/api/test/", headers=headers)
print(request.json())
				
			

Se la chiamata API ha avuto esito positivo, dovremmo visualizzare il seguente messaggio:

				
					{'message': 'You successfully sent a request'}
				
			

Valutazione della copertura dei dati

Per effettuare la valutazione della qualità dei dati di Tiingo, uno degli aspetti più importanti da considerare è la copertura. Ci sono punti dati mancanti? I dati coprono l’intervallo storico corretto? Per raggiungere questo obiettivo in modo visivo possiamo usare il metodo imshow() di Matplotlib.pyplot. Imshow rende i dati come un’immagine RGB o pseudo-colore, ovvero il contenuto di DataFrame verrà visualizzato come un colore a seconda del valore e della mappa dei colori utilizzati. 

Se abbiniamo questo al mascheramento del nostro DataFrame possiamo creare un’immagine che mostra lo spazio vuoto dove mancano i dati, permettendoci di valutare rapidamente se i nostri dati sono completi. Vediamo come usare i primi dieci componenti dell’S&P500 per valutare 10 anni di dati Tiingo e verificare eventuali dati mancanti sui prezzi di chiusura aggiustati. A tale scopo creiamo l’immagine mostrata di seguito. In questa immagine qualsiasi punto dati mancante è chiaramente visibile come uno spazio vuoto.

Valutazione della qualità dei dati di Tiingo

Iniziamo creando un elenco dei primi 10 componenti dell’S&P500. Al momento della stesura di questo articolo si tratta di Apple, Microsoft, Amazon, Google (classe A, GOOGL), Google (classe B, GOOG), Tesla, NVidia, Berkshire Hathaway Inc (classe B), Facebook e United Health Group. Creiamo un dizionario vuoto, quindi passiamo in rassegna i ticker e creiamo una stringa per la chiamata API all’API Tiingo. Quindi chiamiamo l’API e passiamo la risposta al metodo JSON. Il JSON viene quindi convertito in un DataFrame. Infine aggiorniamo il nostro dizionario con il ticker come chiave e DataFrame come valore.

				
					
# Lista dei ticker da scaricare
sp_10 = ['AAPL', 'MSFT', 'AMZN', 'GOOGL', 'GOOG', 'TSLA', 'NVDA', 'BRK-B', 'FB', 'UNH']
# Crea il dizionario di DataFrame
tiingo_data = {}
for ticker in sp_10:
    tk_string = f'https://api.tiingo.com/tiingo/daily/{ticker.lower()}/prices?startDate=2012-01-02&endDate=2022-02-02/'
    response = requests.get(tk_string, headers=headers).json()
    tk_data = {ticker: pd.DataFrame.from_dict(response)}
    tiingo_data.update(tk_data)
				
			

Se ora visualizziamo l’output della variabile, tiingo_data, come mostrato di seguito, possiamo vedere che abbiamo un indice numerico e un timestamp. Per i dati di fine giornata il timestamp è semplicemente 00:00:00.000.

Valutazione della qualità dei dati di Tiingo

Una nota sui fusi orari

I dati di Tiingo vengono solitamente forniti in UTC, tuttavia, se usiamo i dati intraday di Tiingo dobbiamo essere consapevole che The Investors Exchange (IEX) usa il fuso orario America/New_York, che è l’ora standard orientale (EST – UTC-05:00) o l’ora legale orientale (EDT – UTC-04:00) a seconda del periodo dell’anno. La seconda domenica di marzo alle 02:00 EST gli orologi nel fuso orario orientale avanzano alle 03:00. La prima domenica di novembre alle 02:00 EDT gli orologi vengono riportati all’01:00. 

Nel meridiano italiano, l’ultima domenica di marzo alle 02:00 CET inizia l’ora legale (CEST – UTC+02:00). Questo termina l’ultima domenica di ottobre alle 03:00 CEST (o 02:00 CET), che riporta l’Italia all’ora solare (UTC+01:00). Vale la pena dedicare un po’ di tempo alla comprensione dei fusi orari nei dati. Possono essere la fonte di molti errori nel codice.

Una volta che abbiamo il dizionario di DataFrames possiamo convertire la data in un oggetto datetime e impostare l’indice. Poiché in questo caso il timestamp non ha un impatto significativo sulla nostra analisi (stiamo usando dati EOD), lo rimuoviamo dall’indice.

				
					
# Converte le date in formato datetime senza TZ
# Imposta le date come indice per tutti i dataframe nel dizionario
for k in tiingo_data.keys():
    tiingo_data[k]['date'] = pd.to_datetime(tiingo_data[k]['date'])
    tiingo_data[k] = tiingo_data[k].set_index('date').tz_localize(None)
				
			

Se stampiamo il contenuto di tiingo_data vediamo ora che che la data è l’indice e il timestamp è stato rimosso.

data-azionari-tiingo-data-date

I DataFrame che abbiamo creato dai JSON scaricati da Tiingo non hanno righe /record per le date mancanti. Avranno semplicemente righe consecutive di punti dati  inviati dal fornitore di dati. Per determinare se mancano dati, dobbiamo creare un elenco di tutti i giorni con mercati aperti compresi tra le date di inizio e di fine. Alcuni lettori avranno già notato che, sebbene l’intervallo di date richiesto per i dati inizi il 1 marzo 2012, sia le azioni di Classe B di Metaverse (Facebook) che quelle di Google non erano negoziate in quel momento. Le azioni di classe B di Google hanno iniziato a essere negoziate il 27 marzo 2014 e Metaverse (FB) il 18 maggio 2012. Usando un indice di date completo per il periodo di negoziazione, abbiamo intenzionalmente punti dati mancanti per entrambi questi ticker. In questo modo possiamo descrivere come visualizzare la copertura tramite questo metodo.

Creare un calendario di trading è un’abilità essenziale da apprendere se desideri effettuare backtest più realistici. Sebbene Pandas abbia un calendario di trading USFederalHolidayCalendar, non è coerente. Il trading non viene effettuato il Columbus Day o il Veteran’s Day ma viene effettuato il Venerdì Santo. La creazione di un proprio calendario di trading può essere eseguita in Pandas tramite  la classe AbstractHolidayCalendar. Tuttavia, esiste un’eccellente libreria open source Pandas-Market-Calendars, che viene regolarmente aggiornata da dicembre 2016. Offre calendari festivi, di apertura/chiusura anticipata e posticipata per specifici exchange e convenzioni Over The Counter. 

L’ultima versione, la versione 3.4 al momento in cui scrivo, è disponibile solo per l’installazione tramite il gestore di pacchetti Python PIP. Il gestore pacchetti Conda ha una versione precedente che non è compatibile con le versioni successive di Pandas. In genere non è consigliabile utilizzare pip per installare i pacchetti tramite conda poiché questi pacchetti verranno installati su un canale diverso. In questo caso, tuttavia, è necessario ottenere la versione corretta del pacchetto, quindi utilizzeremo pip per installare questo pacchetto nell’ambiente conda.

Il comando pip installa il pacchetto all’interno dell’ambiente anaconda in cui stiamo lavorando.

				
					(py3.8)$ pip install pandas_market_calendars==3.4
				
			

Ora possiamo importare il pacchetto aggiungendolo alle importazioni

				
					
import pandas_market_calendars as mcal
import matplotlib.pyplot as plt
				
			

Di seguito è riportato il codice per ottenere tutti i giorni di negoziazione per la Borsa di New York e per creare un programma di date per l’intervallo di date scelto. Possiamo quindi definire la frequenza con vogliamo i nostri datetimes, ad esempio giornaliera o oraria. Infine rimuoviamo il timestamp dall’indice. Questo viene fatto in modo da poter confrontare le date dei dati  di Tiingo (dove abbiamo già rimosso il timestamp) con le date nel nostro calendario di trading personalizzato. Questo viene salvato con la variabile date_index.

				
					
nyse = mcal.get_calendar('NYSE')
trading_days = nyse.schedule(start_date='2012-01-03', end_date='2022-02-02')
date_index = mcal.date_range(trading_days, frequency='1D').date
				
			
Stampando l’output della variabile date_index otteniamo una serie di date e orari come nell’immagine seguente.
data-azionari-tiingo-mcal-date-index

Ora abbiamo un dizionario di DataFrames che contiene i dati OHLCV scaricati da Tiingo. Nel dizionario le chiavi sono i ticker e i valori sono i DataFrames. Abbiamo anche un date_index di tutti i giorni di negoziazione all’interno del nostro intervallo di date. Per verificare se abbiamo tutti i prezzi di chiusura aggiustati, dobbiamo creare un DataFrame di prezzi di chiusura per ogni titolo azionario dove date_index è l’indice e le colonne sono le serie di prezzi di chiusura di ogni ticker. 

Il Dataframe è creato in modo simile alla serie di prezzi di chiusura creata nel precedente articolo Introduzione ai dati dei mercati finanziari di Stooq. Pertanto descriveremo il codice solo in breve. Creiamo un elenco di serie di Pandas che contengono il prezzo di chiusura aggiustato per ciascun ticker. L’indice di ciascuna serie sarà l’indice della data originale del fornitore di dati. Pertanto avranno lunghezze diverse e non potremo concatenarli. 

Creiamo quindi un oggetto MultiIndex in cui il ticker è il livello 0 e la variabile date_index è il livello 1, che contiene le date di tutti i giorni di negoziazione nell’intervallo di date. Reindicizziamo quindi ciascuna serie dell’elenco riempiendo eventuali valori mancanti con NaN. A tale scopo dobbiamo importare  la libreria numpy. Ora che abbiamo un elenco di serie con lunghezza congruente, possiamo concatenarle con pd.concat().

				
					
import numpy as np
# Crea la lista di serie multIndexed series dai dati dei prezzi adjusted close
ticker_series_list = []
for k,v in tiingo_data.items():
    ticker_series = pd.Series(v['adjClose'].to_list(), index=tiingo_data[k].index, name='AdjClose')
    arrays = [[k]*len(date_index), date_index]
    new_index = pd.MultiIndex.from_arrays(arrays, names=['ticker', 'date'])
    ticker_amend = ticker_series.reindex(new_index, level=1, method=None, fill_value=np.NaN)
    ticker_series_list.append(ticker_amend)

close_series = pd.concat([series for series in ticker_series_list])
				
			

Quindi, come abbiamo mostrato nell’articolo precedente, possiamo scompattarli per diventare un DataFrame MultiIndexed

				
					
tiingo_top10 = close_series.unstack()
				
			

Trasponiamo il DataFrame MultiIndexed, scambiando le righe e le colonne per ottenere il DataFrame finale con il prezzo di chiusura  aggiustato per tutti i ticker in tutti i giorni di  mercati aperti nell’intervallo di date prescelto.

				
					
tiingo_top10 = tiingo_top10.T
				
			

Otteniamo un DataFrame simile a quello mostrato di seguito. Possiamo vedere i NaN presenti sia nella serie dei prezzi di FB che in quella di GOOG, dove non sono disponibili dati.

data-azionari-tiingo-close-df

Dopo avere ottenuto il DataFrame dei prezzo di chiusura aggiustati, possiamo creare l’immagine per effettuare la valutazione della copertura dei dati di Tiingo. Il metodo Imshow permette di tracciare i valori come punti di colore in base alla mappa dei colori scelta. Possiamo semplicemente tracciare i dati tramite il comando plt.imshow(tiingo_top10.values, aspect='auto'). Questo può essere un buon modo per visualizzare aspetti come l’andamento dei prezzi. Tuttavia, quando si ha a che fare con un gran numero di ticker e un ampio intervallo di date, è facile non notare un riquadro vuoto se ci sono numerosi colori diversi. 

Per valutare la qualità dei dati di Tiingo, dobbiamo mascherare i dati in modo che tutte le celle che non contengono NaN vengano modificate in 1. Cioè tutte le celle con un valore numerico saranno semplicemente 1 anziché il prezzo. Possiamo utilizzare i metodi df.mask()df.notna() di Pandas per raggiungere questo obiettivo.

				
					# Sostituisce tutti i valori diversi da NaN con 1 
tiingo_cov = tiingo_top10.mask(tiingo_top10.notna(), 1)
				
			
Possiamo creare l’immagine  tramite imshow per verificare se mancano punti dati. Usiamo imshow con df.values() per restituire un array numpy dei valori nel DataFrame e la parola chiave spect="auto" per riempire l’asse. Questo attributo consente una visualizzazione migliore per un set di dati rispetto all’utilizzo del valore predefinito=”equal”, che prevede che ogni pixel sia visualizzato con proporzioni uguali, ovvero un quadrato.
				
					
plt.imshow(tiingo_cov.values, aspect='auto')
				
			
Valutazione della qualità dei dati di Tiingo

Con un po’ più di elaborazione possiamo ottenere lo stesso istogramma della copertura dei dati ma con colori diversi per ogni titolo e assi etichettati in modo più chiaro.

Usare una mappa di colori

Per creare questo istogramma, possiamo usare lo stesso metodo di  mask ma dobbiamo variare il numero intero che specifichiamo per ciascun titolo. In questo modo possiamo visualizzare un colore diverso per ogni  titolo azionario e uno spazio vuoto per eventuali NaN. Creiamo anche una sottotrama in modo da poter accedere all’oggetto assi e utilizzare il  metodo ax.set_xticklabels()

Usiamo anche una libreria di mappe di colori Palettable di terze parti. Palettable fornisce un gran numero di mappe di colori diverse che possono essere selezionate in base al numero di elementi che desideri colorare e al tipo di mappa di colori che desideri creare. C’è un ottimo articolo qui che descrive come selezionare una mappa dei colori in base ai dati che vogliamo mostrare. Poiché i titoli azionari sono indipendenti, non hanno un ordinamento e il set di dati è considerato qualitativo. In questo caso usiamo una mappa colori qualitativa.

Per creare una maschera con un numero diverso per ogni colonna possiamo enumerare le colonne del DataFrame e applicare la maschera al Dataframe, utilizzando il contatore nel metodo enumerate per modificare il valore della maschera per ciascuna colonna. Il seguente codice applica il numero 1.0 a qualsiasi cella nella prima colonna che non ha un NaN, applicherà 2.0 alle celle nella seconda colonna e così via fino all’ultima colonna.

				
					
for i, col in enumerate(tiingo_top10):
    tiingo_top10[col].mask(tiingo_top10[col].notna(), i+1, inplace=True)
				
			

Otteniamo un DataFrame simile al seguente.

data-azionari-tiingo-cov-df

Per creare la figura inizieremo installando la libreria palettable. Come descritto nella documentazione il pacchetto è disponibile tramite pip. Dobbiamo farlo all’interno dell’ambiente virtuale che abbiamo creato in precedenza.

				
					
(py3.8)$ pip install palettable
				
			

Ora dobbiamo importare la mappa dei colori che abbiamo scelto. Prendiamo la mappa Paired_10 dal modulo palettable.colobrewer.qualitative.

				
					
from palettable.colorbrewer.qualitative import Paired_10

				
			
Ora possiamo creare la sottotrama. Il metodo plt.subplots() di Matplotlib restituisce una tupla di istanze di oggetti figure e axes. Il grafico finale è composto da una figura che incapsula tutte le sottotrame e i singoli oggetti degli assi per ciascuna sottotrama. I singoli oggetti degli assi vengono restituiti come elenco nel secondo elemento della tupla. L’oggetto figure permette di chiamare metodi sull’intera figura come fig.title, mentre l’oggetto axes permette di chiamare metodi sui singoli oggetti assi come ax.set_xticks. Anche se qui stiamo creando solo una singola figura, questo approccio permette di espandere oggetti su più assi, se necessario. Per prima cosa creiamo la figura e la sottotrama, quindi usiamo il metodo imshow() sugli oggetti degli assi specificando la mappa dei colori scelta come parametro. Impostiamo l’interpolazione con “none”, in modo da impedire la sfocatura tra i punti dati nell’immagine finale. Questo effetto sfocato è visibile solo quando usiamo più colori. Creiamo quindi un elenco di ticker dai nomi delle colonne del DataFrame tiingo_top10. Per impostarli come etichette dell’asse x dobbiamo assegnare sia una posizione che un’etichetta. Dato che le etichette degli assi dei grafici precedenti erano semplicemente un conteggio delle colonne indicizzato a zero, possiamo usara 0-9 per individuare le nuove etichette. Quindi impostiamo anche l’etichetta dell’asse y.
				
					
fig, ax = plt.subplots(1, 1)
ax.imshow(tiingo_top10, aspect='auto', cmap=Paired_10.mpl_colormap, interpolation='none')
x_label_list = tiingo_top10.columns.to_list()
ax.set_xticks([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
ax.set_xticklabels(x_label_list)
ax.set_ylabel("Number of Days")
				
			
data-azionari-tiingo-coverage

L’aggiunta dei diversi colori per ciascun titolo insieme alle etichette dell’asse x ha davvero aiutato ad individuare ciascun ticker. Possiamo vedere chiaramente i dati mancanti per GOOG e FB. In questo esempio esaminiamo solo dieci azioni, ma questo metodo può essere applicato a migliaia di ticker per la valutazione della qualitaà dei dati di Tiingo che stiamo scaricato. Invece di passare ore a esplorare DataFrames o eseguire comandi per cercare valori mancanti, l’utilizzo di imshow consente una buona e rapida visibilità di un set di dati.

Gli altri articoli di questa serie

Benvenuto su DataTrading!

Sono Gianluca, ingegnere software e data scientist. Sono appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato DataTrading per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

DataTrading vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Torna in alto
Scroll to Top