Skip to content

Commit

Permalink
lmt and stp order types backtest
Browse files Browse the repository at this point in the history
  • Loading branch information
letianzj committed Aug 12, 2020
1 parent c8516cc commit 6957afb
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 32 deletions.
2 changes: 2 additions & 0 deletions quanttrading2/backtest_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ def _tick_event_handler(self, tick_event):
# data_baord update last, so it still holds price of last tick; for position MtM
# for backtest, strategy pull directly from hist_data; so it doesn't matter
self._data_board.on_tick(tick_event)
# check standing orders, after databoard is updated
self._backtest_brokerage.on_tick(tick_event)

def _order_event_handler(self, order_event):
"""
Expand Down
96 changes: 68 additions & 28 deletions quanttrading2/brokerage/backtest_brokerage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ..event.event import EventType
from ..order.fill_event import FillEvent
from ..order.order_event import OrderEvent
from ..order.order_type import OrderType
from ..order.order_status import OrderStatus


Expand All @@ -19,6 +20,7 @@ def __init__(self, events_engine, data_board):
"""
self._events_engine = events_engine
self._data_board = data_board
self._next_order_id = 1
self._active_orders = {}

# ------------------------------------ private functions -----------------------------#
Expand All @@ -37,47 +39,85 @@ def _calculate_commission(self, full_symbol, fill_price, fill_size):

return commission

def _cross_limit_order(self):
pass
def _try_cross_order(self, order_event, current_price):
if order_event.order_type == OrderType.MARKET:
order_event.order_status = OrderStatus.FILLED
# limit: if buy, limit_price >= current_price; if sell, opposite
elif (order_event.order_type == OrderType.LIMIT) & \
(((order_event.order_size > 0) & (order_event.limit_price >= current_price)) |
((order_event.order_size < 0)&(order_event.limit_price <= current_price))):
order_event.order_status = OrderStatus.FILLED
# stop: if buy, stop_price <= current_price; if sell, opposite
elif (order_event.order_type == OrderType.STOP) & \
(((order_event.order_size > 0) & (order_event.stop_price <= current_price)) |
((order_event.order_size < 0) & (order_event.stop_price >= current_price))):
order_event.order_status = OrderStatus.FILLED

def _cross_stop_order(self):
pass

def _cross_market_order(self):
pass
# -------------------------------- end of private functions -----------------------------#

# -------------------------------------- public functions -------------------------------#
def reset(self):
self._active_orders.clear()
self._next_order_id = 1

def on_tick(self, tick_event):
# check standing (stop) orders
# put trigged into queue
# and remove from standing order list
_remaining_active_orders_id = []
timestamp = tick_event.timestamp
for oid, order_event in self._active_orders.items():
# this should be after data board is updated
current_price = self._data_board.get_last_price(tick_event.full_symbol)
self._try_cross_order(order_event, current_price)

if order_event.order_status == OrderStatus.FILLED:
fill = FillEvent()
fill.order_id = order_event.order_id
fill.fill_id = order_event.order_id
fill.fill_time = self._data_board.get_last_timestamp(order_event.full_symbol)
fill.full_symbol = order_event.full_symbol
fill.fill_size = order_event.order_size
# TODO: use bid/ask to fill short/long
fill.fill_price = current_price
fill.exchange = 'BACKTEST'
fill.commission = self._calculate_commission(fill.full_symbol, fill.fill_price, fill.fill_size)
self._events_engine.put(fill)
else:
_remaining_active_orders_id.append(order_event)

self._active_orders = {k : v for k, v in self._active_orders if k in _remaining_active_orders_id}

def on_tick(self):
pass

def place_order(self, order_event):
"""
immediate fill, no latency or slippage
try immediate fill, no latency or slippage
the alternative is to save the orders and fill on_tick
"""
# TODO: acknowledge the order
order_event.order_status = OrderStatus.FILLED

fill = FillEvent()
fill.order_id = order_event.order_id
fill.fill_id = order_event.order_id
fill.fill_time = self._data_board.get_last_timestamp(order_event.full_symbol)
fill.full_symbol = order_event.full_symbol
fill.fill_size = order_event.order_size
# TODO: use bid/ask to fill short/long
fill.fill_price = self._data_board.get_last_price(order_event.full_symbol)
fill.exchange = 'BACKTEST'
fill.commission = self._calculate_commission(fill.full_symbol, fill.fill_price, fill.fill_size)

self._events_engine.put(fill)
current_price = self._data_board.get_last_price(order_event.full_symbol)
self._try_cross_order(order_event, current_price)

if order_event.order_status == OrderStatus.FILLED:
fill = FillEvent()
fill.order_id = order_event.order_id
fill.fill_id = order_event.order_id
fill.fill_time = self._data_board.get_last_timestamp(order_event.full_symbol)
fill.full_symbol = order_event.full_symbol
fill.fill_size = order_event.order_size
# TODO: use bid/ask to fill short/long
fill.fill_price = current_price
fill.exchange = 'BACKTEST'
fill.commission = self._calculate_commission(fill.full_symbol, fill.fill_price, fill.fill_size)
self._events_engine.put(fill)
else:
order_event.order_status = OrderStatus.ACKNOWLEDGED
self._active_orders[order_event.order_id] = order_event # save standing orders
self._events_engine.put(order_event)


def cancel_order(self, order_id):
"""cancel order is not supported"""
pass
self._active_orders = {k: v for k, v in self._active_orders if k != order_id}

def next_order_id(self):
return 0
return self._next_order_id
# ------------------------------- end of public functions -----------------------------#
2 changes: 1 addition & 1 deletion quanttrading2/gui/ui_main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def _tick_event_handler(self, tick_event):
self._current_time = tick_event.timestamp

self._data_board.on_tick(tick_event) # update databoard
self._order_manager.on_tick(tick_event) # check standing stop orders
self._order_manager.on_tick(tick_event)
self._strategy_manager.on_tick(tick_event) # feed strategies

def _order_status_event_handler(self, order_status_event): # including cancel
Expand Down
3 changes: 0 additions & 3 deletions quanttrading2/order/order_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ def reset(self):

def on_tick(self, tick_event):
"""
check standing (stop) orders
put trigged into queue
and remove from standing order list
"""
pass

Expand Down

0 comments on commit 6957afb

Please sign in to comment.