def commitOrderExecution(self, order, dateTime, fillInfo): if order.getId() is not None: self.__logger.debug('commitOrderExecution, order[%d] = %s' % (order.getId(), str(order))) 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 cancelOrder(self, order): activeOrder = self.__activeOrders.get(order.getId()) if activeOrder is None: common.logger.error("The order is not active anymore") return if activeOrder.isFilled(): common.logger.error( "Can't cancel order that has already been filled") return if 'temp' in activeOrder.getId(): self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "temp order")) return for _ in range(5): try: canceled_order = self.__httpClient.Order.Order_cancel( orderID=order.getId()).result()[0][0] if canceled_order['ordStatus'] == 'Canceled': try: self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "User requested cancellation")) common.logger.info('order canceled: ' + order.getId()) except Exception as e: common.logger.error('cancelOrder1: ' + str(e)) else: self.onExecution(Execution({"data": canceled_order})) break except Exception as e: common.logger.error('cancelOrder: ' + str(e)) if 'Too Many'.lower() in str( e).lower() or 'Service Unavailable'.lower() in str( e).lower() or 'expired'.lower() in str(e).lower(): time.sleep(4) elif 'bad'.lower() in str( e).lower() or 'Invalid'.lower() in str(e).lower(): break elif 'not found'.lower() in str(e).lower(): self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "not found cancellation")) break
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 self.__shares[order.getInstrument()] = self.getShares( order.getInstrument()) + sharesDelta # Let the strategy know that the order was filled. self.__fillStrategy.onOrderFilled(order) # Notify the order update if order.isFilled(): del self.__activeOrders[order.getId()] 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 money to fill order %s" % (order))
def __orderStatusHandler(self, msg): order = self.__activeOrders.get(msg.orderId) if order == None: return #watch out for dupes - don't submit state changes or events if they were already submitted eventType = None if msg.status == 'Filled' and order.getState( ) != broker.Order.State.FILLED: eventType = broker.OrderEvent.Type.FILLED self._unregisterOrder(order) #order.setState(broker.Order.State.FILLED) if msg.status == 'Submitted' and msg.filled > 0: eventType = broker.OrderEvent.Type.PARTIALLY_FILLED #may already be partially filled #if order.getState() != broker.Order.State.PARTIALLY_FILLED: # order.setState(broker.Order.State.PARTIALLY_FILLED) if msg.status == 'Cancelled' and order.getState( ) != broker.Order.State.CANCELED: #self._unregisterOrder(order) eventType = broker.OrderEvent.Type.CANCELED #order.setState(broker.Order.State.CANCELED) self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) # Notify that the order was canceled. self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "User requested cancellation")) orderExecutionInfo = None if eventType == broker.OrderEvent.Type.FILLED or eventType == broker.OrderEvent.Type.PARTIALLY_FILLED: orderExecutionInfo = broker.OrderExecutionInfo( msg.avgFillPrice, abs(msg.filled), 0, datetime.datetime.now()) order.addExecutionInfo(orderExecutionInfo) 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))
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 dispatch(self): # Switch orders from SUBMITTED to ACCEPTED. ordersToProcess = 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))
def _onUserTrades(self, msg_dict): order = self.__activeOrders.get(msg_dict['order_id']) if order is not None: commision = self.getInstrumentTraits().getCommission(msg_dict['instrument_id']) fill_price = msg_dict['price'] volume = msg_dict['volume'] datetime = msg_dict['datetime'] # Update the order. orderExecutionInfo = broker.OrderExecutionInfo(fill_price, abs(volume), commision, 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)) self.__mongo.tradedb.trades.insert_one(msg_dict) # Update cash and shares. self.__api.qryAccount() else: logger.info("Trade %d refered to order %d that is not active" % (int(msg_dict['trade_id']), int(msg_dict['order_id'])))
def __onBarsImpl(self, order, bars): if order.getId() is not None: self.__logger.debug('__onBarsImpl, order[%d] = %s' % (order.getId(), str(order))) # 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()) if order.getId() is not None: self.__logger.debug( '__onBarsImpl, order[%d] = %s, switchState from SUBMITTED -> ACCEPTED' % (order.getId(), str(order))) 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 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 commitOrderExecution(self, order, dateTime, fillInfo): instrument = order.getInstrument() instrumentSymbol = instrument.symbol priceCurrency = instrument.priceCurrency # Calculate deltas. price = fillInfo.getPrice() quantity = fillInfo.getQuantity() if order.isBuy(): cost = price * quantity * -1 assert (cost < 0) baseDelta = quantity else: assert order.isSell() cost = price * quantity assert (cost > 0) baseDelta = quantity * -1 # Update the cost with the commission. commission = self.getCommission().calculate(order, price, quantity) cost -= commission baseBalanceFinal = self.getInstrumentTraits().round( self.getBalance(instrumentSymbol) + baseDelta, instrumentSymbol) quoteBalanceFinal = self.getInstrumentTraits().round( self.getBalance(priceCurrency) + cost, priceCurrency) if quoteBalanceFinal >= 0: # 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._balances[priceCurrency] = quoteBalanceFinal self._balances[instrumentSymbol] = baseBalanceFinal # Let the strategy know that the order was filled. self._fillStrategy.onOrderFilled(self, order) # Notify the order update if order.isFilled(): self._unregisterOrder(order) eventType = broker.OrderEvent.Type.FILLED else: assert order.isPartiallyFilled( ), "Order was neither filled completely nor partially" eventType = broker.OrderEvent.Type.PARTIALLY_FILLED self.notifyOrderEvent( broker.OrderEvent(order, eventType, orderExecutionInfo)) else: action = "buy" if order.isBuy() else "sell" self._logger.debug("Not enough %s to %s %s %s [order %s]" % (priceCurrency, action, order.getQuantity(), instrumentSymbol, order.getId()))
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") del self.__activeOrders[activeOrder.getId()] activeOrder.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent(broker.OrderEvent(activeOrder, broker.OrderEvent.Type.CANCELED, "User requested cancellation"))
def _onOrderAction(self, msg_dict): order = self.__activeOrders.get(msg_dict['order_id']) if msg_dict['action'] == 'canceled': self._unregisterOrder(order) order.switchState(broker.Order.State.CANCELED) # Notify that the order was canceled. self.notifyOrderEvent(broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "User requested cancellation")) # Update cash and shares. self.__api.qryAccount()
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() >= pyalgotrade.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 cancelOrder(self, order): activeOrder = self.__activeOrders.get(order.getId()) if activeOrder is None: common.logger.error("The order is not active anymore") return if activeOrder.isFilled(): common.logger.error( "Can't cancel order that has already been filled") return self._unregisterOrder(activeOrder) activeOrder.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(activeOrder, broker.OrderEvent.Type.CANCELED, "User requested cancellation"))
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 _onUserTrades(self, trades): ret = False for trade in trades: order = self.__activeOrders.get(trade.getOrderId()) if order is not None: filled = order.getFilled() avgPrice = order.getAvgFillPrice() newQuantity = trade.getBTC() - filled if order.isBuy(): newQuantity -= trade.getFee() newQuantity = liveUtils.CoinRound(newQuantity) if newQuantity == 0: continue ret = True newFillPrice = trade.getBTCUSD() if avgPrice is not None: newFillPrice = (newFillPrice * trade.getBTC() - order.getFilled() * avgPrice) / newQuantity newFee = trade.getFee() - order.getCommissions() newDateTime = trade.getDateTime() logger.info('newTrade: price:%f btc:%f fee:%s time:%s' % (newFillPrice, newQuantity, newFee, newDateTime)) # Update cash and shares. self.refreshAccountBalance() # Update the order. orderExecutionInfo = broker.OrderExecutionInfo( newFillPrice, abs(newQuantity), newFee, newDateTime) order.addExecutionInfo(orderExecutionInfo) if trade.isFilled(): order.setState(order.State.FILLED) # order.updateExecutionInfo(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: logger.info("Trade refered to order %d that is not active" % (trade.getOrderId())) return ret
def cancelOrder(self, order): self.__logger.debug('cancelOrder, order = %s' % (str(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) if order.getId() is not None: self.__logger.debug( 'cancelOrder, order[%d] = %s, switchState to CANCELED' % (order.getId(), str(order))) activeOrder.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(activeOrder, broker.OrderEvent.Type.CANCELED, "User requested cancellation"))
def dispatch(self): # Switch orders from SUBMITTED to ACCEPTED. ordersToProcess = 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. while self.__msg_queue.qsize() > 0: msg = self.__msg_queue.get(True, LiveBroker.QUEUE_TIMEOUT) if msg['event_type'] == CTPTdApi.EventType.ON_TRADE: self._onUserTrades(msg) elif msg['event_type'] == CTPTdApi.EventType.ON_QUERY_ACCOUNT: self.refreshAccountBalance(msg) elif msg['event_type'] == CTPTdApi.EventType.ON_ORDER_ACTION: self._onOrderAction(msg)
def submitOrder(self, order): if order.isInitial(): order.setSubmitted(self._getNextOrderId(), self._getCurrentDateTime()) self._registerOrder(order) # Switch from INITIAL -> SUBMITTED if order.getId() is not None: self.__logger.debug( 'submitOrder, order[%d] = %s, switchState from INITIAL -> SUBMITTED' % (order.getId(), str(order))) order.switchState(broker.Order.State.SUBMITTED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.SUBMITTED, None)) self.__logger.debug('submitOrder -> processOrders') self.processOrders() else: raise Exception("The order was already processed")
def dispatch(self): # Switch orders from SUBMITTED to ACCEPTED. ordersToProcess = 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
def _order_filled(self, trade, order): fee = trade.get_fee() # 获取成交价 fillPrice = float(trade.get_avg_price()) # 获取成交量 btcAmount = float(trade.get_amount_original()) # 获取交易时间 dateTime = trade.get_datetime() exe_info = broker.OrderExecutionInfo(fillPrice, abs(btcAmount), fee, dateTime) order.addExecutionInfo(exe_info) if order.isFilled(): eventType = broker.OrderEvent.Type.FILLED else: eventType = broker.OrderEvent.Type.PARTIALLY_FILLED if not order.isActive(): self._unregisterOrder(order) self.notifyOrderEvent(broker.OrderEvent(order, eventType, exe_info))
def __onBars(self, dateTime, bars): # THE ORDER HERE IS VERY IMPORTANT # 1: Let analyzers process bars. self.__notifyAnalyzers(lambda s: s.beforeOnBars(self, bars)) # 2: Let the strategy process current bars and submit orders. self.onBars(bars) # 3: Notify that the bars were processed. self.__barsProcessedEvent.emit(self, bars) try: for key, position in self.getOrderToPosition().copy().items(): for order in position.getActiveOrders().copy(): if order.isCanceled(): self.getBroker().notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "failed to submit")) except Exception as e: self.info('_onbars: ' + str(e))
def __preProcessOrder(self, order, bar_): if order.getId() is not None: self.__logger.debug('__preProcessOrder, order[%d] = %s' % (order.getId(), str(order))) 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) self.__logger.debug( '__preProcessOrder, order[%d] = %s, switchState to CANCELED' % (order.getId(), str(order))) order.switchState(broker.Order.State.CANCELED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, "Expired")) return ret
def onExecution(self, data): order = self.__activeOrders.get(data.getId()) if order is not None: self.onOrder(data) if order.isSubmitted(): try: order.switchState(broker.Order.State.ACCEPTED) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.ACCEPTED, None)) except Exception as e: common.logger.info('onExecution accept: ' + str(e)) fee = 0 fillPrice = data.getPrice() btcAmount = data.getFilledQty() dateTime = data.getDateTime() old_state = order.getState() # Update the order. if btcAmount > 0 and ( order.getExecutionInfo() is None or order.getExecutionInfo().getDateTime() != dateTime) and 'Trade' in \ data.getData().get('execType', 'Trade'): self.updateCash(order, fillPrice, btcAmount) orderExecutionInfo = broker.OrderExecutionInfo( fillPrice, btcAmount, fee, dateTime) order.addExecutionInfo(orderExecutionInfo) if data.getData()['ordStatus'] == 'New' and data.getFilledQty( ) == 0: new_state = broker.Order.State.ACCEPTED orderExecutionInfo = "New" elif order.isFilled(): new_state = broker.Order.State.FILLED elif order.getFilled() != 0: new_state = broker.Order.State.PARTIALLY_FILLED elif data.getData()['ordStatus'] in ['Canceled', 'Rejected' ] and order.getFilled() == 0: new_state = broker.Order.State.CANCELED orderExecutionInfo = data.getData()['text'] else: common.logger.error('onExecution', data.getData()['ordStatus']) try: if new_state != order.getState(): order.switchState(new_state) except Exception as e: common.logger.info('onExecution switchstate: ' + str(e)) if old_state != order.getState(): try: self.notifyOrderEvent( broker.OrderEvent(order, new_state - 1, orderExecutionInfo)) except Exception as e: common.logger.error('onExecution notifyOrderEvent: ' + str(e)) self.notifyOrderEvent( broker.OrderEvent(order, new_state - 1, orderExecutionInfo)) if not order.isActive() and order.getId() in self.__activeOrders: self._unregisterOrder(order)
def _order_cancelled(self, order): order.switchState(broker.Order.State.CANCELED) self._unregisterOrder(order) self.notifyOrderEvent( broker.OrderEvent(order, broker.OrderEvent.Type.CANCELED, '挂单取消'))