Ereignisgesteuertes Backtesting mit Python - Teil IV

Schriftsteller:Gutes, Erstellt: 2019-03-25 14:24:46, aktualisiert:

Die Diskussion der Ereignis-gesteuerten Backtesting-Implementierung hat zuvor die Ereignisschleife, die Ereignisklassenhierarchie und die Datenverarbeitungskomponente betrachtet.

Ein Strategie-Objekt umfasst alle Berechnungen an Marktdaten, die Beratungssignale zu einem Portfolio-Objekt erzeugen. In diesem Stadium in der ereignisgesteuerten Backtester-Entwicklung gibt es kein Konzept eines Indikators oder Filters, wie sie im technischen Handel zu finden sind.

Die Strategiehierarchie ist relativ einfach, da sie aus einer abstrakten Basisklasse mit einer einzigen reinen virtuellen Methode zur Generierung von SignalEvent-Objekten besteht.

# strategy.py

import datetime
import numpy as np
import pandas as pd
import Queue

von abc import ABCMeta, abstrakte Methode

von Ereignisimport SignalEvent Die Strategy abstract Basisklasse definiert einfach eine reine virtuelle Calculate_signals Methode. In abgeleiteten Klassen wird dies verwendet, um die Generierung von SignalEvent-Objekten basierend auf Marktdatenaktualisierungen zu verarbeiten:

# strategy.py

class Strategy(object):
    """
    Strategy is an abstract base class providing an interface for
    all subsequent (inherited) strategy handling objects.

    The goal of a (derived) Strategy object is to generate Signal
    objects for particular symbols based on the inputs of Bars 
    (OLHCVI) generated by a DataHandler object.

    This is designed to work both with historic and live data as
    the Strategy object is agnostic to the data source,
    since it obtains the bar tuples from a queue object.
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def calculate_signals(self):
        """
        Provides the mechanisms to calculate the list of signals.
        """
        raise NotImplementedError("Should implement calculate_signals()")

Die Definition der Strategie ABC ist einfach. Unser erstes Beispiel für die Unterklasse des Strategie-Objekts nutzt eine Buy-and-Hold-Strategie, um die BuyAndHoldStrategy-Klasse zu erstellen. Dies führt einfach zu einer längeren Aufnahme in ein bestimmtes Wertpapier zu einem bestimmten Datum und hält es innerhalb des Portfolios. Somit wird nur ein Signal pro Wertpapier generiert.

Der Hersteller (Initialisierung) erfordert, dass der Datenverarbeiter für den Markt für Balken und das Ereignis-Warteschlangeobjekt:

# strategy.py

class BuyAndHoldStrategy(Strategy):
    """
    This is an extremely simple strategy that goes LONG all of the 
    symbols as soon as a bar is received. It will never exit a position.

    It is primarily used as a testing mechanism for the Strategy class
    as well as a benchmark upon which to compare other strategies.
    """

    def __init__(self, bars, events):
        """
        Initialises the buy and hold strategy.

        Parameters:
        bars - The DataHandler object that provides bar information
        events - The Event Queue object.
        """
        self.bars = bars
        self.symbol_list = self.bars.symbol_list
        self.events = events

        # Once buy & hold signal is given, these are set to True
        self.bought = self._calculate_initial_bought()

Bei der Initialisierung der BuyAndHoldStrategy verfügt das gekaufte Wörterbuchmitglied über eine Reihe von Schlüsseln für jedes Symbol, die alle auf False gesetzt sind. Sobald der Vermögenswert longed ist, wird dieser auf True gesetzt. Im Wesentlichen ermöglicht dies der Strategie zu wissen, ob er auf dem Markt ist oder nicht:

# strategy.py

    def _calculate_initial_bought(self):
        """
        Adds keys to the bought dictionary for all symbols
        and sets them to False.
        """
        bought = {}
        for s in self.symbol_list:
            bought[s] = False
        return bought

Die reine virtuelle Methode calculate_signals wird konkret in dieser Klasse implementiert. Die Methode durchläuft alle Symbole in der Symbolliste und holt den neuesten Balken aus dem Bars-Daten-Handler ab. Sie überprüft dann, ob dieses Symbol gekauft wurde (d. h. ob wir für dieses Symbol auf dem Markt sind oder nicht) und erstellt gegebenenfalls ein einziges SignalEvent-Objekt. Dies wird dann in die Ereigniswarteschlange platziert und das gekaufte Wörterbuch wird für diese spezielle Symbolschlüssel korrekt auf True aktualisiert:

# strategy.py

    def calculate_signals(self, event):
        """
        For "Buy and Hold" we generate a single signal per symbol
        and then no additional signals. This means we are 
        constantly long the market from the date of strategy
        initialisation.

        Parameters
        event - A MarketEvent object. 
        """
        if event.type == 'MARKET':
            for s in self.symbol_list:
                bars = self.bars.get_latest_bars(s, N=1)
                if bars is not None and bars != []:
                    if self.bought[s] == False:
                        # (Symbol, Datetime, Type = LONG, SHORT or EXIT)
                        signal = SignalEvent(bars[0][0], bars[0][1], 'LONG')
                        self.events.put(signal)
                        self.bought[s] = True

Dies ist eindeutig eine einfache Strategie, aber es reicht aus, um die Natur einer ereignisorientierten Strategiehierarchie zu demonstrieren. In folgenden Artikeln werden wir anspruchsvollere Strategien wie einen Paarhandel betrachten.


Mehr