def commitOrderExecution(self, order, dateTime, fillInfo): price = fillInfo.getPrice() quantity = fillInfo.getQuantity() if order.isBuy(): cost = price * quantity * -1 assert(cost < 0) sharesDelta = quantity elif order.isSell(): cost = price * quantity assert(cost > 0) sharesDelta = quantity * -1 else: # Unknown action assert(False) commission = self.getCommission().calculate(order, price, quantity) cost -= commission resultingCash = self.getCash() + cost # Check that we're ok on cash after the commission. if resultingCash >= 0 or self.__allowNegativeCash: # Update the order before updating internal state since addExecutionInfo may raise. # addExecutionInfo should switch the order state. orderExecutionInfo = broker.OrderExecutionInfo(price, quantity, commission, dateTime) order.addExecutionInfo(orderExecutionInfo) # Commit the order execution. self.__cash = resultingCash updatedShares = order.getInstrumentTraits().roundQuantity( self.getShares(order.getInstrument()) + sharesDelta ) if updatedShares == 0: del self.__shares[order.getInstrument()] else: self.__shares[order.getInstrument()] = updatedShares # Let the strategy know that the order was filled. self.__fillStrategy.onOrderFilled(self, order) # Notify the order update if order.isFilled(): self._unregisterOrder(order) self.notifyOrderEvent(broker.OrderEvent(order, broker.OrderEvent.Type.FILLED, orderExecutionInfo)) elif order.isPartiallyFilled(): self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.PARTIALLY_FILLED, orderExecutionInfo) ) else: assert(False) else: self.__logger.debug("Not enough cash to fill %s order [%s] for %s share/s" % ( order.getInstrument(), order.getId(), order.getRemaining() ))
def _onUserTrades(self, trades): for trade in trades: order = self.__activeOrders.get(trade.getOrderId()) if order is not None: fee = trade.getFee() fillPrice = trade.getBTCUSD() btcAmount = trade.getBTC() dateTime = trade.getDateTime() # Update cash and shares. self.refreshAccountBalance() # Update the order. orderExecutionInfo = broker.OrderExecutionInfo( fillPrice, abs(btcAmount), fee, dateTime) order.addExecutionInfo(orderExecutionInfo) if not order.isActive(): self._unregisterOrder(order) # Notify that the order was updated. if order.isFilled(): eventType = broker.OrderEvent.Type.FILLED else: eventType = broker.OrderEvent.Type.PARTIALLY_FILLED self.notifyOrderEvent( broker.OrderEvent(order, eventType, orderExecutionInfo)) else: common.logger.info( "Trade %d refered to order %d that is not active" % (trade.getId(), trade.getOrderId()))
def submitOrder(self, order): if order.isInitial(): order.setSubmitted(self._getNextOrderId(), self._getCurrentDateTime()) self._registerOrder(order) # Switch from INITIAL -> SUBMITTED order.switchState(broker.Order.State.SUBMITTED) self.notifyOrderEvent(broker.OrderEvent(order, broker.OrderEvent.Type.SUBMITTED, None)) else: raise Exception("The order was already processed")
def cancelOrder(self, order): activeOrder = self.__activeOrders.get(order.getId()) if activeOrder is None: raise Exception("The order is not active anymore") if activeOrder.isFilled(): raise Exception("Can't cancel order that has already been filled") self._unregisterOrder(activeOrder) activeOrder.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(activeOrder, broker.OrderEvent.Type.CANCELED, "User requested cancellation") )
def __postProcessOrder(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.getGoodTillCanceled(): expired = False if self.__barFeed.getFrequency() >= quantworks.bar.Frequency.DAY: expired = bar_.getDateTime().date() >= order.getAcceptedDateTime().date() # Cancel the order if it will expire in the next bar. if expired: self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent(broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "Expired"))
def __preProcessOrder(self, order, bar_): ret = True # For non-GTC orders we need to check if the order has expired. if not order.getGoodTillCanceled(): expired = bar_.getDateTime().date() > order.getAcceptedDateTime().date() # Cancel the order if it is expired. if expired: ret = False self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent(broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "Expired")) return ret
def cancelOrder(self, order): activeOrder = self.__activeOrders.get(order.getId()) if activeOrder is None: raise Exception("The order is not active anymore") if activeOrder.isFilled(): raise Exception("Can't cancel order that has already been filled") self.__httpClient.cancelOrder(order.getId()) self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) # Update cash and shares. self.refreshAccountBalance() # Notify that the order was canceled. self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "User requested cancellation"))
def __onBarsImpl(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.getBar(order.getInstrument()) if bar_ is not None: # Switch from SUBMITTED -> ACCEPTED if order.isSubmitted(): order.setAcceptedDateTime(bar_.getDateTime()) order.switchState(broker.Order.State.ACCEPTED) self.notifyOrderEvent(broker.OrderEvent(order, broker.OrderEvent.Type.ACCEPTED, None)) if order.isActive(): # This may trigger orders to be added/removed from __activeOrders. self.__processOrder(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.isCanceled()) assert(order not in self.__activeOrders)
def dispatch(self): # Switch orders from SUBMITTED to ACCEPTED. ordersToProcess = list(self.__activeOrders.values()) for order in ordersToProcess: if order.isSubmitted(): order.switchState(broker.Order.State.ACCEPTED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.ACCEPTED, None)) # Dispatch events from the trade monitor. try: eventType, eventData = self.__tradeMonitor.getQueue().get( True, LiveBroker.QUEUE_TIMEOUT) if eventType == TradeMonitor.ON_USER_TRADE: self._onUserTrades(eventData) else: common.logger.error( "Invalid event received to dispatch: %s - %s" % (eventType, eventData)) except queue.Empty: pass