В предыдущей статье мы описали концепцию обратного теста, основанного на событиях. В остальной части этой серии статей мы сосредоточимся на каждой из отдельных иерархий классов, которые составляют общую систему.
Как было обсуждено в предыдущей статье, торговая система использует две петли while - внешнюю и внутреннюю. Внутренняя петля while обрабатывает захват событий из очереди в памяти, которые затем перенаправляются на соответствующий компонент для последующих действий. В этой инфраструктуре есть четыре типа событий:
Родительский класс называется Event. Он является базовым классом и не обеспечивает никакой функциональности или конкретного интерфейса. В последующих реализациях объекты Event, вероятно, будут развивать большую сложность, и, таким образом, мы будущее-устойчивость дизайна таких систем путем создания иерархии классов.
# 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 наследует от Event и обеспечивает немного больше, чем самоидентификацию, что это событие типа
# 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'
SignalEvent требует символа тикера, временной отметки для генерации и направления для того, чтобы сообщить объекту 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 немного сложнее, чем SignalEvent, поскольку он содержит поле количества в дополнение к вышеупомянутым свойствам SignalEvent. Количество определяется ограничениями портфеля. Кроме того, OrderEvent имеет метод print_order ((), используемый для вывода информации в консоль при необходимости.
# 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)
FillEvent - это событие с наибольшей сложностью. Он содержит временную метку, когда был выполнен ордер, символ ордера и обмен, на котором он был выполнен, количество сделанных акций, фактическую цену покупки и понесенную комиссию.
Комиссия рассчитывается с использованием комиссий Interactive Brokers. Для заказов API США эта комиссия составляет минимум 1,30 доллара США за заказ, с фиксированной ставкой 0,013 доллара США или 0,08 доллара США за акцию в зависимости от того, составляет ли размер торговли менее или выше 500 единиц акций.
# 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
В следующей статье серии мы рассмотрим, как разработать иерархию классов DataHandler рынка, которая позволяет как историческую обратную проверку, так и прямую торговлю через один и тот же интерфейс класса.