In questo articolo continua lo sviluppo di un ambiente di backtesting basato sugli eventi, utilizzando Python. Nel precedente articolo è stata approfondita la gerarchia della classe Portfolio che permette di gestire le posizioni correnti, generare ordini di trading e tenere traccia dei profitti e delle perdite (PnL).
Il porossimo passo è implementare l’esecuzione di questi ordini, creando una gerarchia di classi che rappresenta un meccanismo per la simulazione della gestione degli ordini e, infine, collegarsi ad un broker o ad altri intermediari di mercato.
Esecuzione degli Ordini
L’ExecutionHandler
descritto in questo articolo è estremamente semplice, poiché esegue tutti gli ordini al prezzo corrente di mercato. Questo è altamente irrealistico, ma serve come una buona base di partenza da perfezionare successivamente.
Come per le precedenti gerarchie di classi astratte di base, bisogna importare le proprietà e i decoratori necessari dalla libreria abc
. Inoltre, è necessario importare FillEvent
e OrderEvent
:
# execution.py
import datetime
import queue
from abc import ABCMeta, abstractmethod
from event.event import FillEvent, OrderEvent
La classe ExecutionHandler
è simile alle precedenti classi astratte di base e ha solamente un metodo virtuale, execute_order
:
# execution.py
class ExecutionHandler(object):
"""
La classe astratta ExecutionHandler gestisce l'interazione
tra un insieme di oggetti "ordini" generati da un portafoglio e
l'ultimo set di oggetti Fill che effettivamente si verificano
nel mercato.
Gli handles possono essere utilizzati per creare sottoclassi
con interfacce identiche per broker simulati o broker live.
Questo permette di sottoporre strategie a backtesting in modo
molto simile al motore di live trading.
"""
__metaclass__ = ABCMeta
@abstractmethod
def execute_order(self, event):
"""
Accetta un evento Order e lo esegue, producendo
un evento Fill che viene inserito nella coda degli eventi.
Parametri:
event - Contiene un oggetto Event con informazioni sull'ordine.
"""
raise NotImplementedError("Should implement execute_order()")
Per testare le strategie, è necessario simulare il modo in cui un trade verrà eseguito. L’implementazione più semplice possibile consiste nell’ipotizzare che tutti gli ordini siano stati eseguiti al prezzo corrente di mercato per qualsiasi quantità. Questo è chiaramente estremamente irrealistico e gran parte del lavoro per aumentare il grado di realismo del backtesting consiste nel progettare dei modelli avanzati per simulare lo slippage e il market-impact.
Da notare che all’interno del metodo FillEvent
viene passato un valore pari a None
per l’attributo fill_cost
(vedere la penultima riga in execute_order) come abbiamo descritto per il costo di esecuzione nell’oggetto NaivePortfolio
descritto nell’articolo precedente. In un’implementazione più realistica, si utilizza il valore di dati di mercato “attuali” per ottenere un costo di esecuzione più realistico.
Ho inoltre utilizzato ARCA come exchange, anche se per i scopi di backtesting questo è puramente un segnaposto. In un ambiente di esecuzione dal vivo questo attributo diventa molto più importante:
# execution.py
class SimulatedExecutionHandler(ExecutionHandler):
"""
Il gestore di esecuzione simulato converte semplicemente tutti gli
oggetti Ordine automaticamente negli equivalenti oggetti Fill
senza considerare i problemi di latenza, slittamento e rapporto di
esecuzione (fill-ratio).
Ciò consente un semplice test "first go" di qualsiasi strategia,
prima dell'implementazione con un gestiore di esecuzione più sofisticato.
"""
def __init__(self, events):
"""
Inizializza il gestore, impostando internamente le code degli eventi.
Parametri
events - L'oggetto di coda degli eventi.
"""
self.events = events
def execute_order(self, event):
"""
Converte semplicemente gli oggetti Order in oggetti Fill base,
cioè senza considerare latenza, slittamento o rapporto di esecuzione.
Parametri:
event - Contiene un oggetto Event con informazioni sull'ordine.
"""
if event.type == 'ORDER':
fill_event = FillEvent(datetime.datetime.utcnow(), event.symbol,
'ARCA', event.quantity, event.direction, None)
self.events.put(fill_event)
Questo conclude le gerarchie di classi necessarie per implementare un ambiente di backtesting basato sugli eventi.
Nel prossimo articolo si descriverà come calcolare un insieme di metriche sul rendimento per la strategia oggetto del backtesting.
Per il codice completo riportato in questo articolo, utilizzando il modulo di backtesting event-driven DataBacktest si può consultare il seguente repository di github:
https://github.com/datatrading-info/DataBacktest