def commit_order_execution(self, order, date_time, fill_info): price = fill_info.price quantity = fill_info.quantity if order.is_a_buy_order: cost = price * quantity * -1 assert (cost < 0) shares_delta = quantity elif order.is_a_sell_order: cost = price * quantity assert (cost > 0) shares_delta = quantity * -1 else: # Unknown action assert False commission = self.commission.calculate(order, price, quantity) cost -= commission resulting_cash = self.cash + cost # Check that we're ok on cash after the commission. if resulting_cash >= 0 or self.__allow_negative_cash: # Update the order before updating internal state since add_execution_info may raise. # add_execution_info should switch the order state. order_execution_info = broker.OrderExecutionInfo( price, quantity, commission, date_time) order.add_execution_info(order_execution_info) # Commit the order execution. self.__cash = resulting_cash updated_shares = order.instrument_traits.round_quantity( self.shares(order.instrument) + shares_delta) if updated_shares == 0: del self.__shares[order.instrument] else: self.__shares[order.instrument] = updated_shares # Let the strategy know that the order was filled. self.__fill_strategy.onOrderFilled(self, order) # Notify the order update if order.is_filled: self._unregister_order(order) self.notify_order_event( broker.OrderEvent(order, broker.OrderEvent.Type.FILLED, order_execution_info)) elif order.is_partially_filled: self.notify_order_event( broker.OrderEvent(order, broker.OrderEvent.Type.PARTIALLY_FILLED, order_execution_info)) else: assert False else: self.__logger.debug( "Not enough cash to fill %s order [%s] for %d share/s" % (order.instrument, order.id, order.remaining))
def cancel_order(self, order): active_order = self.__active_orders.get(order.id) if active_order is None: raise Exception("The order is not active anymore") if active_order.is_filled: raise Exception("Can't cancel order that has already been filled") self._unregister_order(active_order) active_order.switch_state(broker.BaseOrder.State.CANCELED) self.notify_order_event( broker.OrderEvent(active_order, broker.OrderEvent.Type.CANCELED, "User requested cancellation"))
def __post_process_order(self, order, bar_): # For non-GTC orders and daily (or greater) bars we need to check if orders should expire right now # before waiting for the next bar. if not order.good_till_canceled: expired = False if self.__barfeed.frequency >= const.Frequency.DAY: expired = bar_.date_time.date( ) >= order.accepted_date_time.date() # Cancel the order if it will expire in the next bar. if expired: self._unregister_order(order) order.switch_state(broker.BaseOrder.State.CANCELED) self.notify_order_event( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "Expired"))
def __pre_process_order(self, order, bar_): ret = True # For non-GTC orders we need to check if the order has expired. if not order.good_till_canceled: expired = bar_.date_time.date() > order.accepted_date_time.date() # Cancel the order if it is expired. if expired: ret = False self._unregister_order(order) order.switch_state(broker.BaseOrder.State.CANCELED) self.notify_order_event( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "Expired")) return ret
def __on_bars_Impl(self, order, bars): # IF WE'RE DEALING WITH MULTIPLE INSTRUMENTS WE SKIP ORDER PROCESSING IF THERE IS NO BAR FOR THE ORDER'S # INSTRUMENT TO GET THE SAME BEHAVIOUR AS IF WERE BE PROCESSING ONLY ONE INSTRUMENT. bar_ = bars.bar(order.instrument) if bar_ is not None: # Switch from SUBMITTED -> ACCEPTED if order.is_submitted: order.setAcceptedDateTime(bar_.date_time) order.switch_state(broker.BaseOrder.State.ACCEPTED) self.notify_order_event( broker.OrderEvent(order, broker.OrderEvent.Type.ACCEPTED, None)) if order.is_active: # This may trigger orders to be added/removed from __active_orders. self.__process_order(order, bar_) else: # If an order is not active it should be because it was canceled in this same loop and it should # have been removed. assert order.is_canceled assert (order not in self.__active_orders)