Tests arrière basés sur des événements avec Python - Partie IV

Auteur:La bonté, Créé: 2019-03-25 14:24:46, mis à jour:

La discussion de l'implémentation de backtesting basé sur des événements a précédemment examiné la boucle d'événements, la hiérarchie des classes d'événements et la composante de traitement des données.

Un objet de stratégie encapsule tous les calculs sur les données du marché qui génèrent des signaux de conseil à un objet de portefeuille. À ce stade du développement du backtester axé sur les événements, il n'y a pas de concept d'indicateur ou de filtre, comme ceux trouvés dans le trading technique.

La hiérarchie de stratégie est relativement simple car elle consiste en une classe de base abstraite avec une seule méthode virtuelle pure pour générer des objets SignalEvent.

# strategy.py

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

de l'abc à l'import ABCMeta, méthode abstraite

depuis l'importation d'événement SignalEvent La classe de base abstraite Strategy définit simplement une méthode virtuelle pure calculate_signals. Dans les classes dérivées, cela est utilisé pour gérer la génération d'objets SignalEvent basés sur des mises à jour de données de marché:

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

La définition de la stratégie ABC est simple. Notre premier exemple de sous-classement de l'objet Stratégie utilise une stratégie d'achat et de détention pour créer la classe BuyAndHoldStrategy. Cela va simplement long dans un titre particulier à une certaine date et le garde dans le portefeuille. Ainsi, un seul signal par titre est jamais généré.

Le constructeur (init) exige que le gestionnaire de données du marché des barres et l'objet de la file d'attente des événements:

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

Lors de l'initialisation de la stratégie BuyAndHold, le membre du dictionnaire acheté dispose d'un ensemble de clés pour chaque symbole qui sont toutes réglées sur False. Une fois que l'actif a été longé, il est alors réglé sur True.

# 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

La méthode virtuelle pure calculate_signals est implémentée concrètement dans cette classe. La méthode fait un boucle sur tous les symboles de la liste de symboles et récupère la dernière barre du gestionnaire de données des barres. Elle vérifie ensuite si ce symbole a été acheté (c'est-à-dire si nous sommes sur le marché pour ce symbole ou non) et si ce n'est pas, crée un seul objet SignalEvent. Ceci est ensuite placé sur la file d'attente d'événements et le dictionnaire acheté est correctement mis à jour en True pour cette clé de symbole particulière:

# 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

Il s'agit clairement d'une stratégie simple, mais elle suffit à démontrer la nature d'une hiérarchie de stratégie basée sur les événements. Dans les articles suivants, nous examinerons des stratégies plus sophistiquées telles qu'un commerce de paires.


Plus de