def testPartialFill(self): order = self.__buildAcceptedLimitOrder(broker.Order.Action.BUY, 2, 11) self.assertEqual(order.getRemaining(), 11) self.assertEqual(order.getFilled(), 0) self.assertEqual(order.getAvgFillPrice(), None) self.assertEqual(order.getExecutionInfo(), None) order.addExecutionInfo(broker.OrderExecutionInfo(1, 8, 0, datetime.datetime.now())) self.assertTrue(order.isPartiallyFilled()) self.assertEqual(order.getRemaining(), 3) self.assertEqual(order.getFilled(), 8) self.assertEqual(order.getAvgFillPrice(), 1) self.assertEqual(order.getExecutionInfo().getQuantity(), 8) self.assertEqual(order.getExecutionInfo().getPrice(), 1) order.addExecutionInfo(broker.OrderExecutionInfo(1.5, 1, 0, datetime.datetime.now())) self.assertTrue(order.isPartiallyFilled()) self.assertEqual(order.getRemaining(), 2) self.assertEqual(order.getFilled(), 9) self.assertEqual(round(order.getAvgFillPrice(), 4), round(1.055555556, 4)) self.assertEqual(order.getExecutionInfo().getQuantity(), 1) self.assertEqual(order.getExecutionInfo().getPrice(), 1.5) order.addExecutionInfo(broker.OrderExecutionInfo(1.123, 2, 0, datetime.datetime.now())) self.assertTrue(order.isFilled()) self.assertEqual(order.getRemaining(), 0) self.assertEqual(order.getFilled(), 11) self.assertEqual(round(order.getAvgFillPrice(), 4), round(1.067818182, 4)) self.assertEqual(order.getExecutionInfo().getQuantity(), 2) self.assertEqual(order.getExecutionInfo().getPrice(), 1.123)
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 commitOrderExecution(self, order, price, quantity, dateTime): if order.getAction() in [broker.Order.Action.BUY, broker.Order.Action.BUY_TO_COVER]: cost = price * quantity * -1 assert(cost < 0) sharesDelta = quantity elif order.getAction() in [broker.Order.Action.SELL, broker.Order.Action.SELL_SHORT]: cost = price * quantity assert(cost > 0) sharesDelta = quantity * -1 else: # Unknown action assert(False) ret = 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: # Commit the order execution. self.setCash(resultingCash) self.__shares[order.getInstrument()] = self.getShares(order.getInstrument()) + sharesDelta ret = True # Update the order. orderExecutionInfo = broker.OrderExecutionInfo(price, quantity, commission, dateTime) order.setExecuted(orderExecutionInfo) return ret
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 _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 __getFilledMarketOrder(self, quantity, price): order = backtesting.MarketOrder(broker.Order.Action.BUY, BaseTestCase.TestInstrument, quantity, False, broker.IntegerTraits()) order.setState(broker.Order.State.ACCEPTED) order.addExecutionInfo( broker.OrderExecutionInfo(price, quantity, 0, datetime.datetime.now())) return order
def testCompleteFillInvalidSize(self): order = self.__buildAcceptedLimitOrder(broker.Order.Action.BUY, 1, 1) with self.assertRaises(Exception): order.addExecutionInfo(broker.OrderExecutionInfo(1, 1.001, 0, datetime.datetime.now())) self.assertTrue(order.isAccepted()) self.assertEqual(order.getRemaining(), 1) self.assertEqual(order.getFilled(), 0) self.assertEqual(order.getAvgFillPrice(), None) self.assertEqual(order.getExecutionInfo(), None)
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 __getFilledMarketOrder(self, quantity, price): order = backtesting.MarketOrder( broker.Order.Action.BUY, INSTRUMENT, quantity, False, backtesting.DefaultInstrumentTraits() ) order.setState(broker.Order.State.ACCEPTED) order.addExecutionInfo(broker.OrderExecutionInfo(price, quantity, 0, datetime.datetime.now())) return order
def testCompleteFill(self): order = self.__buildAcceptedLimitOrder(broker.Order.Action.BUY, 1, 1) self.assertEqual(order.getRemaining(), 1) self.assertEqual(order.getFilled(), 0) self.assertEqual(order.getAvgFillPrice(), None) self.assertEqual(order.getExecutionInfo(), None) order.addExecutionInfo(broker.OrderExecutionInfo(0.9, 1, 0, datetime.datetime.now())) self.assertTrue(order.isFilled()) self.assertEqual(order.getRemaining(), 0) self.assertEqual(order.getFilled(), 1) self.assertEqual(order.getAvgFillPrice(), 0.9)
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): 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 _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 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)