In questo articolo continua lo sviluppo di un ambiente di backtesting basato sugli eventi, utilizzando Python. Nel precedente articolo è stata approfondita la gerarchia della classe Portfolio che permette di gestire le posizioni correnti, generare ordini di trading e tenere traccia dei profitti e delle perdite (PnL). Il porossimo passo è implementare l’esecuzione di questi ordini, creando una gerarchia di classi che rappresenta un meccanismo per la simulazione della gestione degli ordini e, infine, collegarsi ad un broker o ad altri intermediari di mercato. L’ExecutionHandler descritto in questo articolo è estremamente semplice, poiché esegue tutti gli ordini al prezzo corrente di mercato. Questo è altamente irrealistico, ma serve come una buona base di partenza da perfezionare successivamente. Come per le precedenti gerarchie di classi astratte di base, bisogna importare le proprietà e i decoratori necessari dalla libreria abc. Inoltre, è necessario importare FillEvent e OrderEvent:
# execution.py

import datetime
import Queue

from abc import ABCMeta, abstractmethod

from event import FillEvent, OrderEvent
La classe ExecutionHandler è simile alle precedenti classi astratte di base e ha solamente un metodo virtuale, execute_order:
# execution.py

class ExecutionHandler(object):
    """
    The ExecutionHandler abstract class handles the interaction
    between a set of order objects generated by a Portfolio and
    the ultimate set of Fill objects that actually occur in the
    market. 

    The handlers can be used to subclass simulated brokerages
    or live brokerages, with identical interfaces. This allows
    strategies to be backtested in a very similar manner to the
    live trading engine.
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def execute_order(self, event):
        """
        Takes an Order event and executes it, producing
        a Fill event that gets placed onto the Events queue.

        Parameters:
        event - Contains an Event object with order information.
        """
        raise NotImplementedError("Should implement execute_order()")

Per testare le strategie, è necessario simulare il modo in cui un trade verrà eseguito. L’implementazione più semplice possibile consiste nell’ipotizzare che tutti gli ordini siano stati eseguiti al prezzo corrente di mercato per qualsiasi quantità. Questo è chiaramente estremamente irrealistico e gran parte del lavoro per aumentare il grado di realismo del backtesting consiste nel progettare dei modelli avanzati per simulare lo slippage e il market-impact.

Da notare che all’interno del metodo FillEvent viene passato un valore pari a None per l’attributo fill_cost (vedere la penultima riga in execute_order) come abbiamo descritto per il costo di esecuzione nell’oggetto NaivePortfolio descritto nell’articolo precedente. In un’implementazione più realistica, si utilizza il valore di dati di mercato “attuali” per ottenere un costo di esecuzione più realistico.

Ho inoltre utilizzato ARCA come exchange, anche se per i scopi di backtesting questo è puramente un segnaposto. In un ambiente di esecuzione dal vivo questo attributo diventa molto più importante:

# execution.py

class SimulatedExecutionHandler(ExecutionHandler):
    """
    The simulated execution handler simply converts all order
    objects into their equivalent fill objects automatically
    without latency, slippage or fill-ratio issues.

    This allows a straightforward "first go" test of any strategy,
    before implementation with a more sophisticated execution
    handler.
    """
    
    def __init__(self, events):
        """
        Initialises the handler, setting the event queues
        up internally.

        Parameters:
        events - The Queue of Event objects.
        """
        self.events = events

    def execute_order(self, event):
        """
        Simply converts Order objects into Fill objects naively,
        i.e. without any latency, slippage or fill ratio problems.

        Parameters:
        event - Contains an Event object with order information.
        """
        if event.type == 'ORDER':
            fill_event = FillEvent(datetime.datetime.utcnow(), event.symbol,
                                   'ARCA', event.quantity, event.direction, None)
            self.events.put(fill_event)

Questo conclude le gerarchie di classi necessarie per implementare un ambiente di backtesting basato sugli eventi.

Nel prossimo articolo si descriverà come calcolare un insieme di metriche sul rendimento per la strategia oggetto del backtesting.

Recommended Posts