Valutare la qualità dei dati di Tiingo

Valutazione della qualità dei dati di Tiingo

In questa lezione descriviamo come valutare la qualità dei dati di Tiingo, un fornitore di dati e strumenti per il mercato azionario. Abbiamo iniziato a esplorare Tiingo, fondato nel 2014, perché punta a fornire dati accurati, puliti e affidabili. Offrono dati OHLCV per 82.468 titoli globali, 37.319 azioni statunitensi e cinesi, oltre a 45.149 ETF e fondi comuni. Collaborando con IEX, forniscono anche un feed intraday.

Accediamo anche a dati fondamentali su azioni statunitensi, ADR e cinesi, con una copertura in espansione. Analizziamo i dati provenienti da oltre 40 exchange di criptovalute con più di 2100 ticker, insieme a più di 40 ticker FX offerti da banche di livello 1 e dark pool FX. Abbiamo a disposizione anche i dati top of book (bid/ask).

I dati di Tiingo

Una volta registrati per usare l’API di Tiingo, accediamo a oltre 30 anni di dati azionari sia con il piano gratuito che premium, e a 5 anni di dati fondamentali per gli utenti gratuiti (oltre 15 anni per gli utenti premium). Utilizziamo dati provenienti da tre diverse borse valori, che Tiingo integra in un framework proprietario per la pulizia dei dati. 

Questo approccio ci permette di ridurre errori, evitare dati mancanti e garantire la ridondanza. I dati si aggiornano alle 17:30 EST per azioni/ETF e alle 00:00 EST per i NAV dei fondi comuni. Gli aggiornamenti in tempo reale arrivano direttamente da IEX. Tiingo applica le correzioni del cambio durante la sera, fino alla chiusura dei mercati alle 20:00.

Nel piano gratuito non possiamo accedere all’API dei dati fondamentali né all’API Tiingo News, e rispettiamo le seguenti limitazioni API:

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

Con il piano Power User, che costa solo $10 al mese, otteniamo:

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

Con il livello Power User accediamo anche all’API delle notizie e ai dati fondamentali come componente aggiuntivo. Al momento della stesura di questa lezione, questa API si trovava in versione beta, con i titoli del DOW30 disponibili per la valutazione. Entrambi i livelli prevedono un utilizzo personale e interno. Offrono anche licenze commerciali e pacchetti per la ridistribuzione dei dati, disponibili contattando il team vendite di Tiingo.

Valutare la qualità dei dati

Per valutare la qualità dei dati Tiingo, utilizziamo la REST API EOD per accedere ai dati di fine giornata di dieci azioni statunitensi. Creiamo una visualizzazione con il metodo imshow() di Matplotlib per analizzare graficamente la qualità dei dati. Modifichiamo l’immagine includendo una heatmap esterna e aggiungiamo le etichette degli assi in modo chiaro e leggibile.

Convertiamo i dati JSON in un DataFrame Pandas e applichiamo il metodo df.mask() per realizzare la visualizzazione con imshow(). Questo metodo funziona con qualsiasi serie di ticker, attributo di prezzo o intervallo temporale. Il codice che proponiamo si adatta alla risposta fornita dall’API EOD di Tiingo, ma possiamo modificarlo facilmente per lavorare con altri fornitori di dati.

Per facilitare la replica del codice di questa lezione consigliamo di creare un ambiente virtuale usando la distribuzione Python Anaconda. In alternativa, se desideriamo eseguire il codice senza configurare Anaconda, dobbiamo installare le seguenti librerie 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

Per iniziare, ci registriamo su Tiingo e otteniamo una chiave API. Recuperiamo la chiave accedendo al profilo e aprendo la sezione Token dal menu API. Quando inviamo una richiesta all’API di Tiingo, riceviamo una risposta in formato JSON o CSV (il default è JSON). Vediamo come possiamo convertire il file JSON in un DataFrame Pandas. Tiingo offre una guida completa per l’uso dell’API in questa pagina.

Per valutare la qualità dei dati Tiingo all’interno di un DataFrame, importiamo le seguenti librerie Python.

				
					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'}
				
			

Verificare la copertura dei dati

Per valutare la 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.

Valutare la qualità dei dati di Tiingo

Iniziamo creando un elenco dei primi 10 componenti dell’S&P500. Al momento della stesura di questa lezioneo 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.

Valutare la qualità dei dati di Tiingo

Una nota sui fusi orari

Verificare i fusi orari è fondamentale per valutare la qualità dei dati di un vendor. 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. 

Sebbene l’intervallo di date richiesto per i dati inizi il 1 marzo 2012, sia le azioni di Classe B di Meta (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 Meta (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.

Calendario di trading

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

Gestione dei prezzi aggiustati

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 nella lezione 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 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

Valutare la qualità

Dopo avere ottenuto il DataFrame dei prezzo di chiusura aggiustati, possiamo creare l’immagine per valutare la 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')
				
			
Valutare la 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

Creazione del grafico

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 valutare la qualità 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.

Conclusione

L’analisi dei dati Tiingo ci offre un quadro chiaro sull’importanza di valutare la qualità dei dati prima di impiegarli in un contesto di analisi finanziaria o backtest. La copertura temporale, l’assenza di valori mancanti e la precisione nei prezzi aggiustati sono elementi fondamentali da verificare. Il metodo basato su imshow() combinato con un calendario di trading ben costruito ci permette di evidenziare graficamente eventuali lacune nei dataset. 

È cruciale comprendere anche i fusi orari, specialmente con i dati intraday, per evitare errori di interpretazione temporale. L’accesso tramite API, la possibilità di estensione con librerie Python moderne e l’affidabilità complessiva rendono Tiingo un fornitore competitivo. Tuttavia, le limitazioni nei piani gratuiti vanno tenute in considerazione se si pianifica un uso intensivo o professionale. 

L’intera procedura dimostra come un approccio sistematico e riproducibile sia essenziale nella gestione dei dati di mercato. Questo ci consente non solo di individuare i punti critici, ma anche di garantire una base solida per analisi e strategie quantitative.

Il codice completo presentato in questa lezione è disponibile nel seguente repository GitHub: “https://github.com/tradingquant-it/TQData

Torna in alto