Event-Driven Backtesting dengan Python - Bagian II

Penulis:Kebaikan, Dibuat: 2019-03-23 09:13:33, Diperbarui:

Dalam artikel sebelumnya kami menjelaskan konsep backtester event-driven. Sisanya dari seri artikel ini akan berkonsentrasi pada masing-masing hierarki kelas terpisah yang membentuk sistem keseluruhan.

Seperti yang dibahas dalam artikel sebelumnya, sistem perdagangan menggunakan dua loop sementara - satu eksternal dan satu internal. loop sementara internal menangani penangkapan peristiwa dari antrian in-memory, yang kemudian diarahkan ke komponen yang sesuai untuk tindakan berikutnya.

  • MarketEvent - Ini dipicu ketika loop while luar memulai heartbeat baru. Hal ini terjadi ketika objek DataHandler menerima pembaruan baru dari data pasar untuk simbol yang sedang dilacak. Hal ini digunakan untuk memicu objek Strategi menghasilkan sinyal perdagangan baru. Objek acara hanya berisi identifikasi bahwa itu adalah acara pasar, tanpa struktur lain.
  • SignalEvent - Obyek Strategi menggunakan data pasar untuk membuat SignalEvents baru. SignalEvent berisi simbol ticker, timestamp untuk ketika itu dihasilkan dan arah (panjang atau pendek).
  • OrderEvent - Ketika objek portofolio menerima SignalEvents, ia mengevaluasi mereka dalam konteks portofolio yang lebih luas, dalam hal ukuran risiko dan posisi.
  • FillEvent - Ketika ExecutionHandler menerima OrderEvent, ia harus melakukan transaksi order. Setelah order telah dilakukan transaksi, ia menghasilkan FillEvent, yang menggambarkan biaya pembelian atau penjualan serta biaya transaksi, seperti biaya atau slippage.

Kelas induk disebut Event. Ini adalah kelas dasar dan tidak menyediakan fungsionalitas atau antarmuka tertentu. Dalam implementasi berikutnya objek Event kemungkinan akan mengembangkan kompleksitas yang lebih besar dan dengan demikian kita masa depan-bukti desain sistem tersebut dengan membuat hirarki kelas.

# 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

MarketEvent mewarisi dari Event dan memberikan sedikit lebih dari pengidentifikasi diri bahwa itu adalah MARKET jenis acara.

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

Sebuah SignalEvent membutuhkan simbol ticker, timestamp untuk generasi dan arah untuk memberi tahu objek 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

OrderEvent sedikit lebih kompleks dari SignalEvent karena berisi bidang kuantitas selain properti SignalEvent yang disebutkan di atas. Jumlahnya ditentukan oleh kendala Portfolio. Selain itu OrderEvent memiliki metode print_order ((), yang digunakan untuk mengeluarkan informasi ke konsol jika diperlukan.

# 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)

The FillEvent adalah Event dengan kompleksitas terbesar. Ini berisi time stamp untuk ketika pesanan dipenuhi, simbol pesanan dan pertukaran di mana itu dilaksanakan, jumlah saham yang ditransaksi, harga pembelian yang sebenarnya dan komisi yang dikeluarkan.

Komisi ini dihitung menggunakan komisi Interactive Brokers. Untuk pesanan API AS, komisi ini minimal 1,30 USD per pesanan, dengan tingkat tetap 0,013 USD atau 0,08 USD per saham tergantung pada apakah ukuran perdagangan di bawah atau di atas 500 unit saham.

# 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

Dalam artikel berikutnya dari seri ini kita akan mempertimbangkan bagaimana untuk mengembangkan pasar DataHandler kelas hierarki yang memungkinkan baik backtesting historis dan perdagangan langsung, melalui antarmuka kelas yang sama.


Lebih banyak