Esempio n. 1
0
    def orderStatus(
            self, orderId, status, filled, remaining, avgFillPrice,
            permId, parentId, lastFillPrice, clientId, whyHeld,
            mktCapPrice=0.0, lastLiquidity=0):
        key = self.orderKey(clientId, orderId, permId)
        trade = self.trades.get(key)
        if trade:
            oldStatus = trade.orderStatus.status
            new = dict(
                status=status, filled=filled,
                remaining=remaining, avgFillPrice=avgFillPrice,
                permId=permId, parentId=parentId,
                lastFillPrice=lastFillPrice, clientId=clientId,
                whyHeld=whyHeld, mktCapPrice=mktCapPrice,
                lastLiquidity=lastLiquidity)
            curr = trade.orderStatus.dict()
            isChanged = curr != {**curr, **new}
            if isChanged:
                trade.orderStatus.update(**new)
                msg = ''
            elif (status == 'Submitted' and trade.log and
                    trade.log[-1].message == 'Modify'):
                # order modifications are acknowledged
                msg = 'Modified'
            else:
                msg = None

            if msg is not None:
                logEntry = TradeLogEntry(self.lastTime, status, msg)
                trade.log.append(logEntry)
                self._logger.info(f'orderStatus: {trade}')
                self._ib.orderStatusEvent.emit(trade)
                trade.statusEvent.emit(trade)
                if status != oldStatus:
                    if status == OrderStatus.Filled:
                        trade.filledEvent.emit(trade)
                    elif status == OrderStatus.Cancelled:
                        trade.cancelledEvent.emit(trade)
        else:
            self._logger.error(
                'orderStatus: No order found for '
                'orderId %s and clientId %s', orderId, clientId)
Esempio n. 2
0
    def error(self, reqId, errorCode, errorString):
        # https://interactivebrokers.github.io/tws-api/message_codes.html
        warningCodes = {165, 202, 399, 434, 10167}
        isWarning = errorCode in warningCodes or 2100 <= errorCode < 2200
        msg = (
            f'{"Warning" if isWarning else "Error"} '
            f'{errorCode}, reqId {reqId}: {errorString}')
        contract = self._reqId2Contract.get(reqId)
        if contract:
            msg += f', contract: {contract}'

        if isWarning:
            self._logger.info(msg)
        else:
            self._logger.error(msg)
            if reqId in self._futures:
                # the request failed
                self._endReq(reqId)
            elif (self.clientId, reqId) in self.trades:
                # something is wrong with the order, cancel it
                trade = self.trades[(self.clientId, reqId)]
                if not trade.isDone():
                    status = trade.orderStatus.status = OrderStatus.Cancelled
                    logEntry = TradeLogEntry(self.lastTime, status, msg)
                    trade.log.append(logEntry)
                    self._logger.warning(f'Canceled order: {trade}')
                    self.ib.orderStatusEvent.emit(trade)
                    trade.cancelledEvent.emit(trade)
            elif errorCode == 317:
                # Market depth data has been RESET
                ticker = self.reqId2Ticker.get(reqId)
                if ticker:
                    for side, l in ((0, ticker.domAsks), (1, ticker.domBids)):
                        for position in reversed(l):
                            level = l.pop(position)
                            tick = MktDepthData(
                                self.lastTime, position, '', 2,
                                side, level.price, 0)
                            ticker.domTicks.append(tick)

        self.ib.errorEvent.emit(reqId, errorCode, errorString, contract)
Esempio n. 3
0
 def execDetails(
         self, reqId: int, contract: Contract, execution: Execution):
     """
     This wrapper handles both live fills and responses to
     reqExecutions.
     """
     self._logger.info(f'execDetails {execution}')
     if execution.orderId == UNSET_INTEGER:
         # bug in TWS: executions of manual orders have unset value
         execution.orderId = 0
     trade = self.permId2Trade.get(execution.permId)
     if not trade:
         key = self.orderKey(
             execution.clientId, execution.orderId, execution.permId)
         trade = self.trades.get(key)
     if trade and contract == trade.contract:
         contract = trade.contract
     else:
         contract = Contract.create(**dataclassAsDict(contract))
     execId = execution.execId
     isLive = reqId not in self._futures
     time = self.lastTime if isLive else execution.time
     fill = Fill(contract, execution, CommissionReport(), time)
     if execId not in self.fills:
         # first time we see this execution so add it
         self.fills[execId] = fill
         if trade:
             trade.fills.append(fill)
             logEntry = TradeLogEntry(
                 time,
                 trade.orderStatus.status,
                 f'Fill {execution.shares}@{execution.price}')
             trade.log.append(logEntry)
             if isLive:
                 self._logger.info(f'execDetails: {fill}')
                 self.ib.execDetailsEvent.emit(trade, fill)
                 trade.fillEvent(trade, fill)
     if not isLive:
         self._results[reqId].append(fill)
Esempio n. 4
0
 def execDetails(self, reqId, contract, execution):
     """
     This wrapper handles both live fills and responses to reqExecutions.
     """
     if execution.orderId == 2147483647:
         # bug in TWS: executions of manual orders have orderId=2**31 - 1
         execution.orderId = 0
     key = self.orderKey(
         execution.clientId, execution.orderId, execution.permId)
     trade = self.trades.get(key)
     if trade and contract.conId == trade.contract.conId:
         contract = trade.contract
     else:
         contract = self._getContract(contract)
     execId = execution.execId
     execution = Execution(**execution.__dict__)
     execution.time = util.parseIBDatetime(execution.time). \
         astimezone(datetime.timezone.utc)
     isLive = reqId not in self._futures
     time = self.lastTime if isLive else execution.time
     fill = Fill(contract, execution, CommissionReport(), time)
     if execId not in self.fills:
         # first time we see this execution so add it
         self.fills[execId] = fill
         if trade:
             trade.fills.append(fill)
             logEntry = TradeLogEntry(
                 self.lastTime,
                 trade.orderStatus.status,
                 f'Fill {execution.shares}@{execution.price}')
             trade.log.append(logEntry)
             if isLive:
                 self._logger.info(f'execDetails: {fill}')
                 self._ib.execDetailsEvent.emit(trade, fill)
                 trade.fillEvent(trade, fill)
     if not isLive:
         self._results[reqId].append(fill)
Esempio n. 5
0
    def error(self, reqId: int, errorCode: int, errorString: str):
        # https://interactivebrokers.github.io/tws-api/message_codes.html
        warningCodes = {165, 202, 399, 404, 434, 492, 10167}
        isWarning = errorCode in warningCodes or 2100 <= errorCode < 2200
        msg = (
            f'{"Warning" if isWarning else "Error"} '
            f'{errorCode}, reqId {reqId}: {errorString}')
        contract = self._reqId2Contract.get(reqId)
        if contract:
            msg += f', contract: {contract}'

        if isWarning:
            self._logger.info(msg)
        else:
            self._logger.error(msg)
            if reqId in self._futures:
                # the request failed
                if self.ib.RaiseRequestErrors:
                    error = RequestError(reqId, errorCode, errorString)
                    self._endReq(reqId, error, success=False)
                else:
                    self._endReq(reqId)

            elif (self.clientId, reqId) in self.trades:
                # something is wrong with the order, cancel it
                trade = self.trades[(self.clientId, reqId)]
                if not trade.isDone():
                    status = trade.orderStatus.status = OrderStatus.Cancelled
                    logEntry = TradeLogEntry(self.lastTime, status, msg)
                    trade.log.append(logEntry)
                    self._logger.warning(f'Canceled order: {trade}')
                    self.ib.orderStatusEvent.emit(trade)
                    trade.statusEvent.emit(trade)
                    trade.cancelledEvent.emit(trade)

        if errorCode == 165:
            # for scan data subscription there are no longer matching results
            dataList = self.reqId2Subscriber.get(reqId)
            if dataList:
                dataList.clear()
                dataList.updateEvent.emit(dataList)
        elif errorCode == 317:
            # Market depth data has been RESET
            ticker = self.reqId2Ticker.get(reqId)
            if ticker:
                # clear all DOM levels
                ticker.domTicks += [MktDepthData(
                    self.lastTime, 0, '', 2, 0, level.price, 0)
                    for level in ticker.domAsks]
                ticker.domTicks += [MktDepthData(
                    self.lastTime, 0, '', 2, 1, level.price, 0)
                    for level in ticker.domBids]
                ticker.domAsks.clear()
                ticker.domBids.clear()
                self.pendingTickers.add(ticker)
        elif errorCode == 10225:
            # Bust event occurred, current subscription is deactivated.
            # Please resubscribe real-time bars immediately
            bars = self.reqId2Subscriber.get(reqId)
            if isinstance(bars, RealTimeBarList):
                self.ib.client.cancelRealTimeBars(reqId)
                self.ib.client.reqRealTimeBars(
                    reqId, bars.contract, bars.barSize, bars.whatToShow,
                    bars.useRTH, bars.realTimeBarsOptions)
            elif isinstance(bars, BarDataList):
                self.ib.client.cancelHistoricalData(reqId)
                self.ib.client.reqHistoricalData(
                    reqId, bars.contract, bars.endDateTime,
                    bars.durationStr, bars.barSizeSetting, bars.whatToShow,
                    bars.useRTH, bars.formatDate, bars.keepUpToDate,
                    bars.chartOptions)

        self.ib.errorEvent.emit(reqId, errorCode, errorString, contract)