Nella precedente lezione abbiamo introdotto la struttura base di un trading system event-driven. Ora descriviamo le singole gerarchie di classi che compongono il sistema generale. In questa lezione esploriamo la gestione degli eventi in un trading system. Gli oggetti scambiano informazioni proprio attraverso gli eventi.
Come indicato nella lezione precedente, il sistema di trading impiega due loop: uno esterno e uno interno. Il loop interno preleva gli eventi dalla coda in memoria e li trasmette direttamente ai componenti incaricati, che eseguono le azioni necessarie.
Gli Eventi
Nella gestione degli eventi di un trading system identifichiamo almeno quattro tipi fondamentali di eventi:
- MarketEvent: il loop esterno attiva questo evento all’inizio di un nuovo impulso. Il
DataHandler
rileva un aggiornamento dei dati di mercato per tutti i simboli monitorati e il sistema attiva l’oggettoStrategy
, che genera nuovi segnali. L’oggetto Event include solo un’identificazione che segnala un evento di mercato. - SignalEvent: l’oggetto
Strategy
elabora i dati di mercato e crea nuovi SignalEvent. OgniSignalEvent
contiene un simbolo ticker, un timestamp e una direzione (long o short). IlPortfolio
interpreta questi eventi come istruzioni per eseguire i trade. - OrderEvent: il
Portfolio
riceve iSignalEvents
e li valuta nel contesto del portafoglio, considerando il rischio e il dimensionamento. Dopo questa valutazione, il sistema genera un OrderEvent da inviare all’ExecutionHandler
. - FillEvent: quando l’
ExecutionHandler
riceve unOrderEvent
, esegue l’ordine e crea unFillEvent
. Questo evento indica il prezzo effettivo dell’operazione e include i costi di transazione come le commissioni o lo slippage.
La classe Event
Il sistema definisce Event
come classe base. Anche se non fornisce un’interfaccia specifica, funge da punto di partenza per la progettazione. Man mano che la gestione degli eventi diventa più articolata, gli oggetti Event acquisiscono complessità. Per questo costruiamo una gerarchia di classi per definire il comportamento dell’intero sistema.
# event.py
class Event(object):
"""
Event è la classe base che fornisce un'interfaccia per tutti
i tipi di sottoeventi (ereditati), che attiverà ulteriori
eventi nell'infrastruttura di trading.
"""
pass
La classe MarketEventi
La MarketEvent
eredita da Event
e prevede semplicemente di autoidentificare l’evento come di tipo “MARKET”.
# event.py
class MarketEvent(Event):
"""
Gestisce l'evento di ricezione di un nuovo aggiornamento dei
dati di mercato con le corrispondenti barre.
"""
def __init__(self):
"""
Inizializzazione del MarketEvent.
"""
self.type = 'MARKET'
La classe SignalEvent
Il SignalEvent
richiede un simbolo ticker, un timestamp della generazione e una direzione per “avvisare” un oggetto Portfolio
.
# event.py
class SignalEvent(Event):
"""
Gestisce l'evento di invio di un Segnale da un oggetto Strategia.
Questo viene ricevuto da un oggetto Portfolio e si agisce su di esso.
"""
def __init__(self, symbol, datetime, signal_type):
"""
Inizializzazione del SignalEvent.
Parametri:
symbol - Il simbolo del ticker, es. 'GOOG'.
datetime - Il timestamp al quale il segnale è stato generato.
signal_type - 'LONG' o 'SHORT'.
"""
self.type = 'SIGNAL'
self.symbol = symbol
self.datetime = datetime
self.signal_type = signal_type
La classe OrderEvent
La OrderEvent
è leggermente più complessa rispetto alla SignalEvent poiché contiene un campo quantità, oltre alle già citate proprietà di SignalEvent
. La quantità è determinata dai vincoli del portafoglio. Inoltre, OrderEvent ha un metodo print_order()
, utilizzato per inviare le informazioni alla console, se necessario.
# event.py
class OrderEvent(Event):
"""
Gestisce l'evento di invio di un ordine al sistema di esecuzione.
L'ordine contiene un simbolo (ad esempio GOOG), un tipo di ordine
(a mercato o limite), una quantità e una direzione.
"""
def __init__(self, symbol, order_type, quantity, direction):
"""
Inizializza il tipo di ordine, impostando se è un ordine a mercato
('MKT') o un ordine limite ('LMT'), la quantità (integral)
e la sua direzione ('BUY' or 'SELL').
Parametri:
symbol - Lo strumento da tradare.
order_type - 'MKT' o 'LMT' per ordine Market or Limit.
quantity - Intero non negativo per la quantità.
direction - 'BUY' o 'SELL' per long o short.
"""
self.type = 'ORDER'
self.symbol = symbol
self.order_type = order_type
self.quantity = quantity
self.direction = direction
def print_order(self):
"""
Stampa dei valori che compongono l'ordine.
"""
print
"Order: Symbol=%s, Type=%s, Quantity=%s, Direction=%s" % \
(self.symbol, self.order_type, self.quantity, self.direction)
La classe FillEvent
Il FillEvent
è l’evento con la maggiore complessità nella gestione degli eventi di un trading system. Contiene un timestamp di quando è stato effettuato un ordine, il simbolo dell’ordine e l’exchange su cui è stato eseguito, la quantità di azioni negoziate, il prezzo effettivo dell’acquisto e la commissione sostenuta.
Calcoliamo la commissione utilizzando i prezzi di Interactive Brokers. Per l’azionario statunitense questa commissione è pari ad un minimo di 1 USD per ordine, con una tariffa fissa di 0,005 USD per azione.
# event.py
class FillEvent(Event):
"""
Incorpora il concetto di un ordine eseguito, come restituito
da un broker. Memorizza l'effettiva quantità scambiata di
uno strumento e a quale prezzo. Inoltre, memorizza
la commissione del trade applicata dal broker.
"""
def __init__(self, timeindex, symbol, exchange, quantity,
direction, fill_cost, commission=None):
"""
Inizializza l'oggetto FillEvent. Imposta il simbolo, il broker,
la quantità, la direzione, il costo di esecuzione e una
commissione opzionale.
Se la commissione non viene fornita, l'oggetto Fill la calcola
in base alla dimensione del trade e alle commissioni di
Interactive Brokers.
Parametri:
timeindex - La risoluzione delle barre quando l'ordine è stato eseguito.
symbol - Lo strumento che è stato eseguito.
exchange - Il broker/exchange dove l'ordine è stato eseguito.
quantity - La quantità effettivamente scambiata.
direction - La direzione dell'esecuzione ('BUY' o 'SELL')
fill_cost - Il valore nominale in dollari.
commission - La commissione opzionale inviata da IB.
"""
self.type = 'FILL'
self.timeindex = timeindex
self.symbol = symbol
self.exchange = exchange
self.quantity = quantity
self.direction = direction
self.fill_cost = fill_cost
# Calcolo della commissione
if commission is None:
self.commission = self.calculate_ib_commission()
else:
self.commission = commission
def calculate_ib_commission(self):
"""
Calcolo delle commisioni di trading basate sulla struttura
delle fee per la API di Interactive Brokers, in USD.
Non sono incluse le fee di exchange o ECN.
Basata sulla "US API Directed Orders":
https://www.interactivebrokers.com/en/index.php?f=commission&p=stocks2
"""
full_cost = 1.3
if self.quantity <= 300:
full_cost = max(0.35, 0.0035 * self.quantity)
elif self.quantity <= 3000:
full_cost = max(0.35, 0.002 * self.quantity)
elif self.quantity <= 20000:
full_cost = max(0.35, 0.0015 * self.quantity)
elif self.quantity <= 100000:
full_cost = max(0.35, 0.001 * self.quantity)
else: # Maggiore di 100 mila azioni
full_cost = max(0.35, 0.0005 * self.quantity)
# full_cost = min(full_cost, 1.0 / 100.0 * self.quantity * self.fill_cost)
return full_cost
Conclusione
In questa lezione abbiamo definito la struttura per la gestione degli eventi in un trading system e il ruolo dei diversi tipi di eventi in un sistema di trading event-driven. Ogni evento svolge una funzione precisa nel flusso delle operazioni, consentendo agli oggetti del sistema di comunicare in modo modulare ed efficiente. Comprendere questa architettura è fondamentale per costruire sistemi scalabili, realistici e riutilizzabili sia nel backtesting che nel trading live.
Nella prossima lezione vedremo come sviluppare la gerarchia della classe DataHandler
che permetta sia backtesting storico che il live trading, tramite la stessa classe di interfaccia.
Il codice completo presentato in questa lezione, basato sul sistema di trading event-driven TQTradingSystem, è disponibile nel seguente repository GitHub: https://github.com/tradingquant-it/TQTradingSystem.”