우리는 이 기사에서 논의하기 시작한 이벤트 구동 백테스터를 고려 한 지 얼마 되지 않았습니다. VI 부분에서는 역사적인 백테스팅 상황에 작동하는 스탠드 인 ExecutionHandler 모델을 코딩하는 방법을 설명했습니다. 이 기사에서는 실시간 거래 시스템으로 이동하기 위해 대응하는 인터랙티브 브로커 API 핸들러를 코딩 할 것입니다.
나는 이전에 트레이더 워크스테이션을 다운로드하고 인터랙티브 브로커 데모 계정을 생성하는 방법과 IbPy를 사용하여 IB API에 기본 인터페이스를 만드는 방법을 논의했습니다. 이 기사는 이벤트 주도 시스템으로 기본 IbPy 인터페이스를 포장하여 라이브 시장 피드와 결합하면 자동 실행 시스템의 기초를 형성 할 것입니다.
IBExecutionHandler 클래스의 본질적인 아이디어는 이벤트 큐에서 OrderEvent 인스턴스를 수신하고 IbPy 라이브러리를 사용하여 인터랙티브 브로커스 주문 API에 직접 실행하는 것입니다. 클래스는 API를 통해 다시 전송된
클래스 자체는 실행 최적화 논리뿐만 아니라 정교한 오류 처리와 함께 상당히 복잡해질 수 있습니다. 그러나, 나는 당신이 주요 아이디어를 볼 수 있도록 비교적 간단하게 유지하도록 선택했습니다. 그리고 당신의 특정 거래 스타일에 맞는 방향으로 확장합니다.
항상 그렇듯이 첫 번째 작업은 파이썬 파일을 만들고 필요한 라이브러리를 가져오는 것입니다. 이 파일은 ib_execution.py라고 불리며 다른 이벤트 주도 파일과 같은 디렉토리에 있습니다.
우리는 필요한 날짜/시간 처리 라이브러리, IbPy 객체 및 IBExecutionHandler에 의해 처리되는 특정 이벤트 객체를 가져옵니다:
# ib_execution.py
import datetime
import time
from ib.ext.Contract import Contract
from ib.ext.Order import Order
from ib.opt import ibConnection, message
from event import FillEvent, OrderEvent
from execution import ExecutionHandler
이제 IBExecutionHandler 클래스를 정의합니다.init이 컨스트럭터는 먼저 이벤트 큐에 대한 지식을 필요로 합니다. 또한 order_routing의 사양을 요구합니다. 이
메소드 내에서 FillEvent 인스턴스를 생성하는 데 나중에 사용해야 할 fill_dict 사전을 만듭니다. 또한 인터랙티브 브로커 API에 연결 정보를 저장하기 위해 tws_conn 연결 객체를 만듭니다. 또한 중복을 피하기 위해 모든 후속 명령을 추적하는 초기 기본 명령어_id을 만들어야합니다. 마지막으로 메시지 핸들러를 등록합니다.
# ib_execution.py
class IBExecutionHandler(ExecutionHandler):
"""
Handles order execution via the Interactive Brokers
API, for use against accounts when trading live
directly.
"""
def __init__(self, events,
order_routing="SMART",
currency="USD"):
"""
Initialises the IBExecutionHandler instance.
"""
self.events = events
self.order_routing = order_routing
self.currency = currency
self.fill_dict = {}
self.tws_conn = self.create_tws_connection()
self.order_id = self.create_initial_order_id()
self.register_handlers()
IB API는 메시지 기반 이벤트 시스템을 활용하여 우리 클래스가 이벤트 기반 백테스터와 비슷한 방식으로 특정 메시지에 특정 방식으로 응답할 수 있습니다. _error_handler 메소드를 통해 터미널에 출력하는 것 이외에 실제 오류 처리 (단순함을 위해) 를 포함하지 않았습니다.
다른 한편으로, _reply_handler 메소드는 FillEvent 인스턴스가 생성되어야하는지 결정하는 데 사용됩니다. 메소드는
만약
# ib_execution.py
def _error_handler(self, msg):
"""
Handles the capturing of error messages
"""
# Currently no error handling.
print "Server Error: %s" % msg
def _reply_handler(self, msg):
"""
Handles of server replies
"""
# Handle open order orderId processing
if msg.typeName == "openOrder" and \
msg.orderId == self.order_id and \
not self.fill_dict.has_key(msg.orderId):
self.create_fill_dict_entry(msg)
# Handle Fills
if msg.typeName == "orderStatus" and \
msg.status == "Filled" and \
self.fill_dict[msg.orderId]["filled"] == False:
self.create_fill(msg)
print "Server Response: %s, %s\n" % (msg.typeName, msg)
다음 메소드인 create_tws_connection는 IbPy ibConnection 객체를 사용하여 IB API에 연결을 만듭니다. 기본 포트는 7496이고 기본 클라이언트 ID는 10입니다. 객체가 생성되면 연결 메소드를 호출하여 연결을 수행합니다.
# ib_execution.py
def create_tws_connection(self):
"""
Connect to the Trader Workstation (TWS) running on the
usual port of 7496, with a clientId of 10.
The clientId is chosen by us and we will need
separate IDs for both the execution connection and
market data connection, if the latter is used elsewhere.
"""
tws_conn = ibConnection()
tws_conn.connect()
return tws_conn
별도의 주문을 추적하기 위해 (트래킹 풀의 목적으로) 다음 메소드 create_initial_order_id를 사용합니다. I
# ib_execution.py
def create_initial_order_id(self):
"""
Creates the initial order ID used for Interactive
Brokers to keep track of submitted orders.
"""
# There is scope for more logic here, but we
# will use "1" as the default for now.
return 1
다음 메소드, register_handlers는 단순히 TWS 연결으로 위에서 정의된 오류 및 응답 처리 메소드를 등록합니다.
# ib_execution.py
def register_handlers(self):
"""
Register the error and server reply
message handling functions.
"""
# Assign the error handling function defined above
# to the TWS connection
self.tws_conn.register(self._error_handler, 'Error')
# Assign all of the server reply messages to the
# reply_handler function defined above
self.tws_conn.registerAll(self._reply_handler)
IbPy를 사용하는 이전 튜토리얼과 마찬가지로 우리는 계약 인스턴스를 생성하고 그 다음 IB API에 전송되는 주문 인스턴스와 결합해야합니다. 다음 방법인 create_contract은 이 쌍의 첫 번째 구성 요소를 생성합니다. 틱어 기호, 증권 유형 (예: 주식 또는 미래), 거래소 / 주요 거래소 및 통화를 기대합니다. 계약 인스턴스를 반환합니다.
# ib_execution.py
def create_contract(self, symbol, sec_type, exch, prim_exch, curr):
"""
Create a Contract object defining what will
be purchased, at which exchange and in which currency.
symbol - The ticker symbol for the contract
sec_type - The security type for the contract ('STK' is 'stock')
exch - The exchange to carry out the contract on
prim_exch - The primary exchange to carry out the contract on
curr - The currency in which to purchase the contract
"""
contract = Contract()
contract.m_symbol = symbol
contract.m_secType = sec_type
contract.m_exchange = exch
contract.m_primaryExch = prim_exch
contract.m_currency = curr
return contract
다음 메소드인 create_order는 쌍의 두 번째 구성 요소인 Order 인스턴스를 생성합니다. 명령 유형 (예를 들어 시장 또는 제한), 거래 할 자산의 양 및
# ib_execution.py
def create_order(self, order_type, quantity, action):
"""
Create an Order object (Market/Limit) to go long/short.
order_type - 'MKT', 'LMT' for Market or Limit orders
quantity - Integral number of assets to order
action - 'BUY' or 'SELL'
"""
order = Order()
order.m_orderType = order_type
order.m_totalQuantity = quantity
order.m_action = action
return order
특정 주문 ID에 대한 FillEvent 인스턴스의 중복을 피하기 위해, 우리는 특정 주문 ID와 일치하는 키를 저장하기 위해 fill_dict라는 사전을 사용합니다. Fill가 생성되면 특정 주문 ID에 대한 항목의
# ib_execution.py
def create_fill_dict_entry(self, msg):
"""
Creates an entry in the Fill Dictionary that lists
orderIds and provides security information. This is
needed for the event-driven behaviour of the IB
server message behaviour.
"""
self.fill_dict[msg.orderId] = {
"symbol": msg.contract.m_symbol,
"exchange": msg.contract.m_exchange,
"direction": msg.order.m_action,
"filled": False
}
다음 메소드, create_fill, 실제로 FillEvent 인스턴스를 만들고 이벤트 대기열에 배치합니다:
# ib_execution.py
def create_fill(self, msg):
"""
Handles the creation of the FillEvent that will be
placed onto the events queue subsequent to an order
being filled.
"""
fd = self.fill_dict[msg.orderId]
# Prepare the fill data
symbol = fd["symbol"]
exchange = fd["exchange"]
filled = msg.filled
direction = fd["direction"]
fill_cost = msg.avgFillPrice
# Create a fill event object
fill = FillEvent(
datetime.datetime.utcnow(), symbol,
exchange, filled, direction, fill_cost
)
# Make sure that multiple messages don't create
# additional fills.
self.fill_dict[msg.orderId]["filled"] = True
# Place the fill event onto the event queue
self.events.put(fill_event)
이제 전술한 모든 메소드가 구현된 후 ExecutionHandler 추상 기본 클래스에서 execute_order 메소드를 대체해야 합니다. 이 메소드는 실제로 IB API로 주문 배치를 수행합니다.
먼저 이 메소드에 수신되는 이벤트가 실제로 OrderEvent인지 확인하고 그 다음 각각의 매개 변수와 함께 계약 및 Order 객체를 준비합니다. 둘 다 생성되면 연결 객체의 IbPy 메소드 placeOrder는 연관된 order_id로 호출됩니다.
이 라인의 제거는 API의 불일치 행동으로 이어집니다, 적어도 내 시스템에서!
마지막으로, 우리는 주문 ID를 증대하여 주문을 중복하지 않도록합니다.
# ib_execution.py
def execute_order(self, event):
"""
Creates the necessary InteractiveBrokers order object
and submits it to IB via their API.
The results are then queried in order to generate a
corresponding Fill object, which is placed back on
the event queue.
Parameters:
event - Contains an Event object with order information.
"""
if event.type == 'ORDER':
# Prepare the parameters for the asset order
asset = event.symbol
asset_type = "STK"
order_type = event.order_type
quantity = event.quantity
direction = event.direction
# Create the Interactive Brokers contract via the
# passed Order event
ib_contract = self.create_contract(
asset, asset_type, self.order_routing,
self.order_routing, self.currency
)
# Create the Interactive Brokers order via the
# passed Order event
ib_order = self.create_order(
order_type, quantity, direction
)
# Use the connection to the send the order to IB
self.tws_conn.placeOrder(
self.order_id, ib_contract, ib_order
)
# NOTE: This following line is crucial.
# It ensures the order goes through!
time.sleep(1)
# Increment the order ID for this session
self.order_id += 1
이 클래스는 인터랙티브 브로커스 실행 핸들러의 기초를 이루고 있으며, 배트테스팅에만 적합한 시뮬레이션 실행 핸들러 대신 사용할 수 있습니다. 그러나 IB 핸들러를 사용할 수 있기 전에, 역 테스트 시스템의 역사적인 데이터 피드 핸들러를 대체하기 위해 라이브 시장 피드 핸들러를 만드는 것이 필요합니다. 이것은 미래의 기사의 주제입니다.
이 방법으로 우리는 백테스트와 라이브 시스템에서 가능한 한 많은 것을 재사용하여 코드