Nel precedente articolo abbiamo introdotto la struttura base di un ambiente di backtesting event-driven. Il resto di questa serie di articoli si concentrerà su ciascuna delle gerarchie di classi  che costituiscono il sistema generale. In questo articolo i descrivono gli Eventi e come questi possono essere usati per scambiare informazioni tra gli oggetti.

Come discusso nel precedente articolo, il sistema di trading utilizza due loop: uno esterno e uno interno. Il loop interno gestisce l’acquisizione degli eventi da una coda in memoria, e il loro smistamento verso gli specific componenti che gestiscono la conseguente azione.

In questo sistema ci sono quattro tipi di eventi:

  • MarketEvent: viene attivato quando il loop esterno inizia un nuovo “impulso”. Si verifica quando l’oggetto DataHandler riceve un nuovo aggiornamento dei dati di mercato per tutti i simboli che sono attualmente monitorati. Viene utilizzato per attivare l’oggetto Strategy che genera nuovi segnali di trading. L’oggetto Event contiene semplicemente un’identificazione che si tratta di un evento di mercato, senza altre strutture.
  • SignalEvent: l’oggetto Strategy utilizza i dati di mercato per creare nuovi SignalEvent. SignalEvents contiene un simbolo ticker, il timestamp di quando è stato generato e una direzione (long o short). I SignalEvents sono utilizzati dall’oggetto Portfolio come consigli su come effettuare i trade.
  • OrderEvent: quando un oggetto Portfolio riceve i SignalEvents, questi sono valutati nel contesto generale del portfolio, in termini di rischio e dimensionamento della posizione. Questo processo genera un OrderEvents che verrà inviato a un ExecutionHandler.
  • FillEvent: dopo la ricezione di un OrderEvent, l’ExecutionHandler deve eseguire l’ordine. Una volta che un ordine è stato eseguito, genera un FillEvent, che descrive il costo di acquisto o vendita, nonché i costi di transazione, come le commissioni o lo slippage.

La classe genitore è chiamata Event. È una classe base e non fornisce alcuna funzionalità o interfaccia specifica. Nelle implementazioni successive gli oggetti Event svilupperanno una maggiore complessità e quindi stiamo definendo la progettazione di tali sistemi creando una gerarchia di classi.

# event.py

class Event(object):
    """
    Event is base class providing an interface for all subsequent 
    (inherited) events, that will trigger further events in the 
    trading infrastructure.   
    """
    pass
La classe MarketEvent eredita da Event e prevede semplicemente di autoidentificare l’evento come di tipo “MARKET”.
# event.py

class MarketEvent(Event):
    """
    Handles the event of receiving a new market update with 
    corresponding bars.
    """

    def __init__(self):
        """
        Initialises the MarketEvent.
        """
        self.type = 'MARKET'
La classe SignalEvent richiede un simbolo ticker, un timestamp della generazione e una direzione per “avvisare” un oggetto Portfolio.
# event.py

class SignalEvent(Event):
    """
    Handles the event of sending a Signal from a Strategy object.
    This is received by a Portfolio object and acted upon.
    """
    
    def __init__(self, symbol, datetime, signal_type):
        """
        Initialises the SignalEvent.

        Parameters:
        symbol - The ticker symbol, e.g. 'GOOG'.
        datetime - The timestamp at which the signal was generated.
        signal_type - 'LONG' or 'SHORT'.
        """
        
        self.type = 'SIGNAL'
        self.symbol = symbol
        self.datetime = datetime
        self.signal_type = signal_type
La classe 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):
    """
    Handles the event of sending an Order to an execution system.
    The order contains a symbol (e.g. GOOG), a type (market or limit),
    quantity and a direction.
    """

    def __init__(self, symbol, order_type, quantity, direction):
        """
        Initialises the order type, setting whether it is
        a Market order ('MKT') or Limit order ('LMT'), has
        a quantity (integral) and its direction ('BUY' or
        'SELL').

        Parameters:
        symbol - The instrument to trade.
        order_type - 'MKT' or 'LMT' for Market or Limit.
        quantity - Non-negative integer for quantity.
        direction - 'BUY' or 'SELL' for long or short.
        """
        
        self.type = 'ORDER'
        self.symbol = symbol
        self.order_type = order_type
        self.quantity = quantity
        self.direction = direction

    def print_order(self):
        """
        Outputs the values within the Order.
        """
        print "Order: Symbol=%s, Type=%s, Quantity=%s, Direction=%s" % \
            (self.symbol, self.order_type, self.quantity, self.direction)
La classe FillEvent è l’evento con la maggiore complessità. 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. La commissione viene calcolata utilizzando le commissioni di Interactive Brokers. Per l’azionario statunitenze 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):
    """
    Encapsulates the notion of a Filled Order, as returned
    from a brokerage. Stores the quantity of an instrument
    actually filled and at what price. In addition, stores
    the commission of the trade from the brokerage.
    """

    def __init__(self, timeindex, symbol, exchange, quantity, 
                 direction, fill_cost, commission=None):
        """
        Initialises the FillEvent object. Sets the symbol, exchange,
        quantity, direction, cost of fill and an optional 
        commission.

        If commission is not provided, the Fill object will
        calculate it based on the trade size and Interactive
        Brokers fees.

        Parameters:
        timeindex - The bar-resolution when the order was filled.
        symbol - The instrument which was filled.
        exchange - The exchange where the order was filled.
        quantity - The filled quantity.
        direction - The direction of fill ('BUY' or 'SELL')
        fill_cost - The holdings value in dollars.
        commission - An optional commission sent from IB.
        """
        
        self.type = 'FILL'
        self.timeindex = timeindex
        self.symbol = symbol
        self.exchange = exchange
        self.quantity = quantity
        self.direction = direction
        self.fill_cost = fill_cost

        # Calculate commission
        if commission is None:
            self.commission = self.calculate_ib_commission()
        else:
            self.commission = commission

    def calculate_ib_commission(self):
        """
        Calculates the fees of trading based on an Interactive
        Brokers fee structure for API, in USD.

        This does not include exchange or ECN fees.

        Based on "US API Directed Orders":
        https://www.interactivebrokers.com/en/index.php?f=commission&p=stocks2
        """
        full_cost = 1.3
        if self.quantity <= 500:
            full_cost = max(1.3, 0.013 * self.quantity)
        else: # Greater than 500
            full_cost = max(1.3, 0.008 * self.quantity)
        full_cost = min(full_cost, 0.5 / 100.0 * self.quantity * self.fill_cost)
        return full_cost
Nel prossimo articolo della serie vedremo come sviluppare la gerarchia della classe DataHandler che permetta sia backtesting storico che il live trading, tramite la stessa classe di interfaccia.

Recommended Posts