In questo articolo descriviamo come applicare una logica per fermare l’esecuzione di script di Backtrader e quindi fermare il trading live delle strategie di trading algoritmico implementate con Python.
Quando l’algoritmo di una strategia passa da un ambiente di backtest al trading con un conto live (sia esso in demo o in reale), ci sono una serie di aspetti extra che devono essere considerati. Ad esempio, come monitorare le prestazioni dell’algoritmo? Durante il backtesting, è possibile generare i report di performance alla fine di un’esecuzione. Tuttavia, durante il trading dal vivo, abbiamo bisogno di report giornalieri o in real-time. Un altro aspetto da considerare è la modalità per fermare l’esecuzione dell’algoritmo? Dato che i dati in tempo reale non devono avere un endpoint definito a priori, quando arriva il momento di spegnerli, sarebbe meglio farlo in modo organizzato invece che uccidere il processo! Infine, quando usiamo un conto reale è saggio prevedere come gestire guasti imprevisti (come un’interruzione di corrente). Questi sono solo alcuni esempi, sono sicuro che chiunque può pensarne molti altri.
Questo articolo non può coprire tutti gli aspetti sopra descritti, quindi ci concentriamo a come arrestare manualmente un algoritmo in esecuzione live.
Segnali
I segnali costituiscono il cuore del codice di arresto dell’algoritmo. Pertanto, vale la pena dedicare un breve paragrafo per descrivere alcune informazioni di base in modo di avere maggiore comprensione del codice. I segnali sono eventi del sistema operativo che sono inviati a un programma per informarlo di qualcosa di importante che deve essere risolto prima di continuare il normale flusso di lavoro del programma. Una volta inviato, il segnale interrompe qualsiasi processo/programma in esecuzione e costringe il processo/programma a gestire immediatamente il segnale. Ci sono diversi segnali che possono essere inviati. Ognuno ha il proprio significato e un comportamento predefinito che viene eseguito quando si riceve il segnale. Se il comportamento predefinito non è quello desiderato, si può specificare all’interno di un programma la logica prevista per gestire il segnale in modo personalizzato. Molto intuitivamente, il codice per gestire un segnale è chiamato handler.
Ora che abbiamo un po’ di background, possiamo chiederci quale segnale dobbiamo usare e come lo inviamo? Oltre al sistema operativo che invia i segnali, i segnali possono anche essere inviati da un programma o manualmente dall’utente finale. Un esempio che probabilmente abbiamo già fatto innumerevoli volte senza rendertene conto è premere CTRL + C, questa combinazione di tasti invia un segnale SIGINT (interrupt) a un programma. Di solito, quando premiamo CTRL + C durante l’esecuzione di uno script Python, si genera un errore KeyboardInterrupt
. Questo è il comportamento predefinito. Dobbiamo sovrascrivere questo comportamento predefinito per implementare il corretto arresto dello script. In un mondo ideale, inviamo SIGINT tramite qualche altro metodo allo script in esecuzione come un processo in background (o demone). Tuttavia, per semplicità e solo per gettare le basi, manteniamo le cose semplici e premiamo CTRL + C.
Documentazione Python
Lavorare con i segnali in Python è un gioco da ragazzi grazie a un pratico modulo integrato in Python. Dobbiamo importare questo modulo nel nostro codice. Per ulteriori informazioni sul modulo, possiamo consultare la documentazione ufficiale di Python disponibile al seguente link:
https://docs.python.org/3/library/signal.html
Ora che siamo in grado di captare un segnale in arrivo, dobbiamo pensare a come gestirlo tramitel l’handler. Fortunatamente, Backtrader ha un utile metodo che ci può aiutare.
Arresto di backtrader
Come accennato in precedenza, Backtrader ha un metodo integrato che ci aiuta a fermare l’esecuzione di un l’algoritmo. Dobbiamo chiamare il metodo runstop()
dall’interno della strategia per interrompere il prima possibile tutti i thread in esecuzione. E’ possibile consultare la seguente documentazione.
https://www.backtrader.com/docu/cerebro#backtrader.Cerebro.runstop
Durante i miei test, ho scoperto che questo metodo mi permette di fermare cerebro e tracciare un grafico dei risultati.
Quindi tutto ciò che dobbiamo fare è mettere insieme queste due logiche e abbiamo finito!
Fermare il Trading Live
Il seguente codice mostra come fermare il trading live di una strategia implementata con backtrader. L’esempio è stato scritto per funzionare con lo store Oanda di Backtrader. Tuttavia, non c’è motivo per cui non possa essere facilmente adattato allo store IB. Quando copi il codice, assicurati di aggiornare la chiave API e il numero di account con il tuo. Altrimenti, non funzionerà. Inoltre, se sei nuovo nel trading dal vivo con Oanda, vale la pena sottolineare che lo store Oanda di Backtraders utilizza la vecchia API Oandapy. Ciò significa che dovrai utilizzare un account Oanda legacy per gli algoritmi Backtrader.
import backtrader as bt
from datetime import datetime
import signal
class sigStrategy(bt.Strategy):
def __init__(self):
self.count = 0
signal.signal(signal.SIGINT, self.sigstop)
def next(self):
print('{}: O {}, H {}, L {}, C {}'.format(
self.data.datetime.datetime(),
self.data.open[0],
self.data.high[0],
self.data.low[0],
self.data.close[0]
))
def sigstop(self, a, b):
print('Stopping Backtrader')
self.env.runstop()
apikey = 'INSERT YOUR API KEY'
acc = 'INSERT YOUR ACC NUM'
inst = 'GBP_USD'
# Creo un istanza di cerebro
cerebro = bt.Cerebro()
# Setup Oanda
oandastore = bt.stores.OandaStore(token=apikey, account=acc, practice=True)
# broker = oandastore.getbroker()
# cerebro.setbroker(broker)
data = oandastore.getdata(dataname=inst, backfill_start=True, timeframe=bt.TimeFrame.Ticks, compression=1)
# Sostituisco i dati nell'ambiente di test così possiamo agire più velocemente
cerebro.resampledata(data, name=inst, timeframe=bt.TimeFrame.Minutes, compression=1)
# Aggiungo la strategia
cerebro.addstrategy(sigStrategy)
# Esecuzione del motore
cerebro.run()
# Grafico dei risultati
cerebro.plot(style='candlestick')
Commento
Il codice precedente è stato volutamente mantenuto semplice. Non farà altro che stampare i dati OHLC per ogni chiamata di next()
. Questo ci permette di concentrarci sulle logiche di arresto. La prima cosa che dobbiamo fare (a parte importare il modulo degli segnali) è creare un metodo per gestire il segnale all’interno della strategia. Questo metodo è eseguito quando si riceve un segnale SIGINT. Possiamo denominare il metodo come preferiamo e può contenere tutti i passaggi che desideriamo. Per fermare l’algoritmo è sufficiente chiamare self.env.runstop()
alla fine di esso.
L’unica cosa da fare dopo è associare il metodo al segnale. Questo è effettuato durante l’inizializzazione della strategia. Si richiama la funzione signal.signal()
e al suo interno passiamo due argomenti. Il primo argomento è il numero del segnale da ascoltare. Il secondo è il gestore da chiamare quando viene ricevuto il segnale. Niente di più semplice.
Una parola di avvertimento
Prestare attenzione quando si lavora con i segnali. È possibile finire in uno stato in cui non è possibile terminare il programma perché si è ignorato il comportamento predefinito e non si è terminato il programma.
Codice completo
In questo articolo abbiamo descritto come applicare una logica per fermare l’esecuzione di script di Backtrader e quindi fermare il trading live delle strategie di trading algoritmico implementate con Python. Per il codice completo riportato in questo articolo, si può consultare il seguente repository di github:
https://github.com/datatrading-info/BackTrader