This article continues the discussion of event-driven backtesters in Python. In the previous article we considered a portfolio class hierarchy that handled current positions, generated trading orders and kept track of profit and loss (PnL).
In this article we will study the execution of these orders, by creating a class hierarchy that will represent a simulated order handling mechanism and ultimately tie into a brokerage or other means of market connectivity.
ExecutionHandler described here is exceedingly simple, since it fills all orders at the current market price. This is highly unrealistic, but serves as a good baseline for improvement.
As with the previous abstract base class hierarchies, we must import the necessary properties and decorators from the
abc library. In addition we need to import the
# execution.py import datetime import Queue from abc import ABCMeta, abstractmethod from event import FillEvent, OrderEvent
ExecutionHandler is similar to previous abstract base classes and simply has one pure virtual method,
# 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()")
In order to backtest strategies we need to simulate how a trade will be transacted. The simplest possible implementation is assumes all orders are filled at the current market price for all quantities. This is clearly extremely unrealistic and a big part of improving backtest realism will come from designing more sophisticated models of slippage and market impact.
Note that the
FillEvent is given a value of
None for the
fill_cost (see the penultimate line in
execute_order) as the we have already taken care of the cost of fill in the
NaivePortfolio object described in the previous article. In a more realistic implementation we would make use of the "current" market data value to obtain a realistic fill cost.
I have simply utilised ARCA as the exchange although for backtesting purposes this is purely a placeholder. In a live execution environment this venue dependence would be far more important:
# 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)
This concludes the class hierarchies necessary to produce an event-driven backtester. In the next article we will discuss how to calculate a set of performance metrics for the backtested strategy.