Exemple #1
0
    def __str__(self):
        if self.requestedOrder is not None:  # IBridgePy created orders
            ans = '{ibpyOrderId=%s status=%s order=%s contract=%s}' % (
                self._ibpyOrderId, self.status, print_IBCpp_order(self.requestedOrder),
                print_IBCpp_contract(self.requestedContract))
        else:  # orders are called-back from IB server
            if self.get_value_by_tag('whyHeld') == '':
                if self.openOrderRecord.order.orderId not in [0, -1]:
                    ans = '{ibpyOrderId=%s status=%s order=%s contract=%s}' % (
                        self.getIbpyOrderId(), self.status, print_IBCpp_order(self.openOrderRecord.order),
                        print_IBCpp_contract(self.openOrderRecord.contract))
                else:
                    ans = '{permId=%s status=%s order=%s contract=%s}' % (
                        self.getIbpyOrderId(), self.status, print_IBCpp_order(self.openOrderRecord.order),
                        print_IBCpp_contract(self.openOrderRecord.contract))
            else:
                if self.openOrderRecord.order.orderId not in [0, -1]:
                    ans = '{ibpyOrderId=%s status=%s order=%s contract=%s whyHeld=%s}' % (
                        self.getIbpyOrderId(), self.status, print_IBCpp_order(self.openOrderRecord.order),
                        print_IBCpp_contract(self.openOrderRecord.contract), self.get_value_by_tag('whyHeld'))
                else:
                    ans = '{permId=%s status=%s order=%s contract=%s  whyHeld=%s}' % (
                        self.getIbpyOrderId(), self.status, print_IBCpp_order(self.openOrderRecord.order),
                        print_IBCpp_contract(self.openOrderRecord.contract), self.get_value_by_tag('whyHeld'))

        return ans
    def _placeOrderHelper(self, int_orderId, ibpyRequest, contract,
                          ibcppOrder):
        ibpyOrderId = self._idConverter.fromIBtoBroker(int_orderId)

        # Set for ending flat.
        # Otherwise, the following line in broker_client_factory::CallBacks::orderStatus will not be able to find a reqId
        # reqId = self.activeRequests.find_reqId_by_int_orderId(int_orderId)
        ibpyRequest.param['int_orderId'] = int_orderId

        # Register ibpyOrderId in SingleTrader so that it can search accountCode by incoming int_orderId
        self._singleTrader.set_from_send_req_to_server(self.name,
                                                       ibcppOrder.account,
                                                       ibpyOrderId)

        # IBCpp function
        self.placeOrder(int_orderId, contract, ibcppOrder)
        self._log.debug(
            __name__ +
            '::placeOrderWrapper: int_orderId=%s contract=%s ibcppOrder=%s' %
            (int_orderId, print_IBCpp_contract(contract),
             print_IBCpp_order(ibcppOrder)))

        # Only IB order may not follow up.
        # If do not follow up on order, just set ending flag and assign ibpyOrderId here.
        if not ibpyRequest.followUp:
            ibpyRequest.returnedResult = ibpyOrderId
            ibpyRequest.status = ReqAttr.Status.COMPLETED
Exemple #3
0
    def execDetails(self, reqId, contract, execution):
        """
        !!!!!! reqId is always -1 based on experiences
        :param reqId:
        :param contract:
        :param execution:
        :return:
        """
        self._log.debug(__name__ +
                        '::execDetails: reqId=%s contract=%s execution=%s' %
                        (reqId, print_IBCpp_contract(contract),
                         print_IBCpp_execution(execution)))
        int_orderId = execution.orderId
        ibpyOrderId = self._idConverter.fromIBtoBroker(int_orderId)
        accountCode = execution.acctNumber
        self._singleTrader.set_execDetails(
            self.brokerName, accountCode,
            ExecDetailsRecord(ibpyOrderId, contract, execution))

        # IB will not invoke updateAccountValue immediately after execDetails
        # http://www.ibridgepy.com/knowledge-base/#Q_What_functions_will_be_called_back_from_IB_server_and_what_is_the_sequence_of_call_backs_after_an_order_is_executed
        # To make sure the user know the rough cashValue, IBridgePy has to simulate them.
        # The accurate value will be updated by IB regular updateAccountValue every 3 minutes.
        # The positionValue and portfolioValues are not simulated for live trading because the user just care about cashValue to buy other contracts
        # These values will be updated by IB regular updateAccountValue every 3 minutes.
        currentCashValue = self._singleTrader.get_account_info(
            self.brokerName, accountCode, 'TotalCashValue')
        cashChange = execution.shares * float(execution.price)
        if execution.side == 'BOT':
            currentCashValue -= cashChange
        elif execution.side == 'SLD':
            currentCashValue += cashChange
        self.updateAccountValue('TotalCashValue', currentCashValue, 'USD',
                                accountCode)
Exemple #4
0
    def placeOrderWrapper(self, contract, order, ibpyRequest):
        self._validate_contract(contract, 'placeOrderWrapper')
        self._log.info('Place Order to %s security=%s order=%s' % (self.name, print_IBCpp_contract(contract), print_IBCpp_order(order)))

        tdOrder = OrderConverter().fromIBtoTD(contract, order)
        try:
            ibpyOrderId = self._tdClient.place_orders(order.account, tdOrder)
        except RuntimeError as e:
            if 'buying power' in str(e):
                raise NotEnoughFund()
            else:
                raise RuntimeError(e)
        if ibpyOrderId is None:
            raise RuntimeError('Place order to TD failed but there is not RuntimeError.')
        self._log.info('Order was placed to %s successfully. ibpyOrderId=%s' % (self.name, ibpyOrderId))

        # Register int_orderId in _idConverter so that brokerClient::CallBack::orderStatus knows how to handle int_orderId
        int_orderId = self._idConverter.fromBrokerToIB(ibpyOrderId)
        self._idConverter.setRelationship(int_orderId, ibpyOrderId)

        # Set for ending flat.
        # Otherwise, the following line in broker_client_factory::CallBacks::orderStatus will not be able to find a reqId
        # reqId = self.activeRequests.find_reqId_by_int_orderId(int_orderId)
        ibpyRequest.param['int_orderId'] = int_orderId

        # Register ibpyOrderId in SingleTrader so that it can search accountCode by incoming int_orderId
        self._singleTrader.set_from_send_req_to_server(self.name, order.account, ibpyOrderId)

        # IBCpp function
        order.orderId = int_orderId
        self.simulateOpenOrder(int_orderId, contract, order, IBCpp.OrderState(),
                               from_contract_to_security(contract).full_print())  # IBCpp function
        # IBCpp function, this is the ending flag for PlaceOrder
        self.simulateOrderStatus(int_orderId, 'Submitted', 0, order.totalQuantity, 0.0, 0, 0, 0, 0, '')
Exemple #5
0
    def order_status_monitor(self,
                             ibpyOrderId,
                             target_status,
                             waitingTimeInSeconds=30):
        self._log.notset(
            __name__ +
            '::order_status_monitor: ibpyOrderId=%s target_status=%s' %
            (ibpyOrderId, target_status))
        timer = dt.datetime.now()
        while True:
            time.sleep(0.1)
            self._brokerClient.processMessagesWrapper(self.get_datetime())

            if (dt.datetime.now() -
                    timer).total_seconds() <= waitingTimeInSeconds:
                tmp_status = self.get_order(ibpyOrderId).status
                if isinstance(target_status, str):
                    if tmp_status == target_status:
                        return
                elif isinstance(target_status, list):
                    if tmp_status in target_status:
                        return
            else:
                self._log.error(
                    __name__ +
                    '::order_status_monitor: EXIT, waiting time is too long, >%i'
                    % (waitingTimeInSeconds, ))
                order = self.get_order(ibpyOrderId)
                contract = order.contract
                self._log.error(
                    __name__ +
                    '::order_status_monitor: EXIT, ibpyOrderId=%s status=%s contract=%s'
                    % (ibpyOrderId, order.status,
                       print_IBCpp_contract(contract)))
                raise OrderStatusNotConfirmed()
    def placeOrderWrapper(self, contract, order, ibpyRequest):
        self._log.debug(
            __name__ + '::placeOrderWrapper: contract=%s order=%s' %
            (print_IBCpp_contract(contract), print_IBCpp_order(order)))

        if isinstance(order.orderId, int):
            int_orderId = order.orderId
        else:
            int_orderId = self.use_next_id()
            order.orderId = int_orderId

        # Set for ending flat.
        # Otherwise, the following line in broker_client_factory::CallBacks::orderStatus will not be able to find a reqId
        # reqId = self.activeRequests.find_reqId_by_int_orderId(int_orderId)
        ibpyRequest.param['int_orderId'] = int_orderId

        ibpyOrderId = self._idConverter.fromIBtoBroker(int_orderId)

        # Register ibpyOrderId in SingleTrader so that it can search accountCode by incoming int_orderId
        self._singleTrader.set_from_send_req_to_server(self.name,
                                                       order.account,
                                                       ibpyOrderId)

        self.orderToBeProcessed[int_orderId] = (contract, order)
        self.simulateOpenOrder(
            int_orderId, contract, order, IBCpp.OrderState(),
            from_contract_to_security(contract).full_print())  # IBCpp function
        self.simulateOrderStatus(int_orderId, 'Submitted', 0,
                                 order.totalQuantity, 0.0, 0, 0, 0, 0,
                                 '')  # IBCpp function
        self.simulate_process_order(self.get_datetime())
 def _validate_contract(self, contract, caller):
     if contract.secType != 'STK' or contract.currency != 'USD':
         self._log.error(
             __name__ +
             '::%s: EXIT, cannot handle this contract=%s, only STK right now'
             % (
                 caller,
                 print_IBCpp_contract(contract),
             ))
         exit()
    def reqHistoricalDataWrapper(self, reqId, contract, str_endTime, goBack,
                                 barSize, whatToShow, useRTH, formatDate):
        # self._log.info(__name__ + '::reqHistoricalDataWrapper: reqId=%s contract=%s endTime=%s goBack=%s barSize=%s' % (reqId, print_IBCpp_contract(contract), endTime, goBack, barSize))
        # https://developer.tdameritrade.com/content/price-history-samples
        self._validate_contract(contract, 'reqHistoricalDataWrapper')
        freq, freqType = BarSizeConverter().fromIBtoTD(barSize)
        pe, peType = GoBackConverter().fromIBtoTD(goBack, barSize)
        if str_endTime:
            str_endTime = dt.datetime.strptime(
                str_endTime, "%Y%m%d %H:%M:%S %Z"
            )  # string -> dt.datetime; strptime silently ignores timezone!!!
            str_endTime = pytz.timezone('UTC').localize(str_endTime)
            endDate = int(dt_to_epoch(str_endTime)) * 1000
            ans = self._tdClient.history(str(contract.symbol),
                                         periodType=peType,
                                         period=pe,
                                         frequencyType=freqType,
                                         frequency=freq,
                                         endDate=endDate)
        else:
            ans = self._tdClient.history(str(contract.symbol),
                                         periodType=peType,
                                         period=pe,
                                         frequencyType=freqType,
                                         frequency=freq)

        if 'error' in ans:
            self._log.error(
                __name__ +
                '::reqHistoricalDataWrapper: EXIT, cannot handle contract=%s endTime=%s goBack=%s barSize=%s'
                %
                (print_IBCpp_contract(contract), str_endTime, goBack, barSize))
            self._log.error(
                __name__ +
                '::reqHistoricalDataWrapper: EXIT, cannot handle periodType=%s period=%s frequencyType=%s frequency=%s'
                % (peType, pe, freqType, freq))
            exit()
        hist = ans['candles']
        for row in hist:
            epoc = int(float(str(row['datetime'])))
            if epoc > 1e10:
                epoc /= 1000.0
            self.simulateHistoricalData(
                reqId,
                str(epoc),
                float(row['open']),
                float(row['high']),
                float(row['low']),
                float(row['close']),
                int(float(row['volume'])),
                1,  # barCount
                0.0,  # WAP
                1)  # hasGap
        self.simulateHistoricalData(reqId, 'finished', 0.0, 0.0, 0.0, 0.0, 1,
                                    1, 0.0, 1)
Exemple #9
0
 def reqMktDataWrapper(self, reqId, contract, genericTickList, snapshot):
     """
     Just ignore reqMktData because the real time prices will be simulated
     :param reqId:
     :param contract:
     :param genericTickList:
     :param snapshot:
     :return:
     """
     self._log.debug(__name__ + '::reqMktDataWrapper: reqId=%s contract=%s genericTickList=%s snapshot=%s'
                     % (str(reqId, ), print_IBCpp_contract(contract), str(genericTickList), str(snapshot)))
     self._activeRequests.set_a_request_of_a_reqId_to_a_status(reqId, ReqAttr.Status.COMPLETED)
     self.processMessagesWrapper(self._timeGenerator.get_current_time())
Exemple #10
0
    def reqHistoricalDataWrapper(self, reqId, contract, endTime, goBack,
                                 barSize, whatToShow, useRTH, formatDate):
        if barSize not in ['1 day', '5 mins', '10 mins']:
            self._log.error(
                __name__ +
                '::reqHistoricalDataWrapper: EXIT, Robinhood does not support barSize=%s'
                % (barSize, ))
        interval = BarSizeConverter().fromIBtoRB(barSize)
        span = GoBackConverter().fromIBtoRB(goBack)
        self._log.notset(
            __name__ +
            '::reqHistoricalDataWrapper: contract=%s interval=%s span=%s' %
            (print_IBCpp_contract(contract), interval, span))
        ans = self._robinhoodClient.get_historical_quotes(
            contract.symbol, interval, span)

        hist = None
        try:
            hist = ans['results'][0]['historicals']
        except KeyError:
            self._log.error(__name__ +
                            '::reqHistoricalDataWrapper: EXIT, ans=%s' %
                            (ans, ))
            exit()

        for row in hist:
            t = dt.datetime.strptime(row['begins_at'], "%Y-%m-%dT%H:%M:%SZ")
            epoc = dt_to_epoch(t)
            if formatDate == 2:
                date = epoc
            else:
                date = epoch_to_dt(float(epoc))
                date = date.strftime(
                    "%Y%m%d  %H:%M:%S"
                )  # Must be UTC because requested time was cast to UTC

            self.simulateHistoricalData(
                reqId,
                str(date),
                float(row['open_price']),
                float(row['high_price']),
                float(row['low_price']),
                float(row['close_price']),
                int(float(row['volume'])),
                1,  # barCount
                0.0,  # WAP
                1)  # hasGap
        self.simulateHistoricalData(reqId, 'finished', 0.0, 0.0, 0.0, 0.0, 1,
                                    1, 0.0, 1)
Exemple #11
0
 def __str__(self):
     ans = '{reqId=%s;status=%s;reqType=%s;followUp=%s;' % (
         self.reqId, self.status, self.reqType, self.followUp)
     tmp = '{'
     for key in self.param:
         if key == 'order':
             tmp += 'order:%s,' % (print_IBCpp_order(self.param[key]))
         elif key == 'contract':
             tmp += 'contract:%s,' % (print_IBCpp_contract(self.param[key]))
         elif key == 'security':
             tmp += 'security:%s,' % (self.param[key].full_print())
         else:
             tmp += '%s:%s,' % (key, self.param[key])
     tmp += '}'
     ans += 'param=%s}' % (tmp, )
     return ans
Exemple #12
0
 def bondContractDetails(self, reqId, contractDetails):
     """
     IB callback function to receive str_security info
     """
     self._log.info(__name__ + '::bondContractDetails:' + str(reqId))
     aRequest = self._activeRequests.get_by_reqId_otherwise_exit(reqId)
     newRow = pd.DataFrame(
         {
             'right': contractDetails.summary.right,
             'strike': float(contractDetails.summary.strike),
             # 'expiry':dt.datetime.strptime(contractDetails.summary.expiry, '%Y%m%d'),
             'expiry': contractDetails.summary.expiry,
             'contractName': print_IBCpp_contract(contractDetails.summary),
             'str_security': contractDetails.summary,
             'multiplier': contractDetails.summary.multiplier,
             'contractDetails': contractDetails
         },
         index=[len(aRequest.returnedResult)])
     aRequest.returnedResult = aRequest.returnedResult.append(newRow)
Exemple #13
0
 def position(self, accountCode, contract, amount, cost_basis):
     """
     call back function of IB C++ API which updates the position of a security
     of a account
     """
     self._log.debug(
         __name__ +
         '::position: accountCode=%s contract=%s amount=%s cost_basis=%s' %
         (accountCode, print_IBCpp_contract(contract), amount, cost_basis))
     # Conclusion: called-back position contract may or may not have exchange info,
     # never see primaryExchange.
     # STK has exchange, CASH does not, FUT does not
     # if contract.exchange != '':
     #    self._log.error(__name__ + '::position: EXIT, contract has exchange=%s' % (print_contract(contract),))
     #    exit()
     security = stripe_exchange_primaryExchange_from_contract(contract)
     self._singleTrader.set_position(
         self.brokerName, accountCode,
         PositionRecord(security.full_print(), amount, cost_basis,
                        contract))
    def placeOrderWrapper(self, contract, ibcppOrder, ibpyRequest):
        self._check_connectivity_reconn_if_needed()
        orderId = ibcppOrder.orderId
        if isinstance(orderId, int) and orderId != 0:
            self._log.debug(__name__ + '::placeOrderWrapper: orderId=%s' %
                            (ibcppOrder.orderId, ))
            int_orderId = ibcppOrder.orderId
        else:
            self.request_data(
                ReqIds()
            )  # For IBinsync, nextId needs updates every time when self._nextId is needed.
            int_orderId = self._nextId.useOne()
            ibcppOrder.orderId = int_orderId
            self._log.debug(__name__ +
                            '::placeOrderWrapper: reqIds and then orderId=%s' %
                            (int_orderId, ))
        ibpyOrderId = self._idConverter.fromIBtoBroker(int_orderId)

        # Set for ending flat.
        # Otherwise, the following line in broker_client_factory::CallBacks::orderStatus will not be able to find a reqId
        # reqId = self.activeRequests.find_reqId_by_int_orderId(int_orderId)
        ibpyRequest.param['int_orderId'] = int_orderId

        # Register ibpyOrderId in SingleTrader so that it can search accountCode by incoming int_orderId
        self._singleTrader.set_from_send_req_to_server(self.name,
                                                       ibcppOrder.account,
                                                       ibpyOrderId)

        # IBCpp function
        self.placeOrder(int_orderId, contract, ibcppOrder)
        self._log.debug(
            __name__ +
            '::placeOrderWrapper: int_orderId=%s contract=%s ibcppOrder=%s' %
            (int_orderId, print_IBCpp_contract(contract),
             print_IBCpp_order(ibcppOrder)))

        # Only IB order may not follow up.
        # If do not follow up on order, just set ending flag and assign ibpyOrderId here.
        if not ibpyRequest.followUp:
            ibpyRequest.returnedResult = ibpyOrderId
            ibpyRequest.status = ReqAttr.Status.COMPLETED
Exemple #15
0
 def scannerData(self, reqId, rank, contractDetails, distance, benchmark,
                 projection, legsStr):
     self._log.debug(
         __name__ +
         '::scannerData: reqId=%s rank=%s contractDetails.summary%s distance=%s benchmark=%s project=%s legsStr=%s'
         % (reqId, rank, print_IBCpp_contract(contractDetails.summary),
            distance, benchmark, projection, legsStr))
     aRequest = self._activeRequests.get_by_reqId_otherwise_exit(reqId)
     security = from_contract_to_security(contractDetails.summary)
     newRow = pd.DataFrame(
         {
             'rank': rank,
             # 'contractDetails': contractDetails,
             'security': security,
             # 'distance': distance,
             # 'benchmark': benchmark,
             # 'projection': projection,
             # 'legsStr': legsStr
         },
         index=[len(aRequest.returnedResult)])
     aRequest.returnedResult = aRequest.returnedResult.append(newRow)
Exemple #16
0
    def openOrder(self, int_ibOrderId, contract, order, orderState):
        """
        call back function of IB C++ API which updates the open orders
        """
        self._log.debug(
            __name__ +
            '::openOrder: ibOrderId=%i contract=%s order=%s orderState=%s' %
            (int_ibOrderId, print_IBCpp_contract(contract),
             print_IBCpp_order(order), print_IBCpp_orderState(orderState)))

        # For IB only:
        # The int_ibOrderId is 0 or -1 if the order is not placed by IB clients
        # Then, create a string value for it using permId
        # IBridgePy will not touch these orders and the created value is not registered in idConverter
        if int_ibOrderId in [-1]:
            str_ibpyOrderId = 'permIDatIB%s' % (order.permId, )
        else:
            str_ibpyOrderId = self._idConverter.fromIBtoBroker(int_ibOrderId)
        self._singleTrader.set_openOrder(
            self.brokerName, order.account,
            OpenOrderRecord(str_ibpyOrderId, contract, order, orderState))
Exemple #17
0
 def contractDetails(self, reqId, contractDetails):
     """
     IB callback function to receive str_security info
     """
     self._log.notset(__name__ +
                      '::contractDetails: reqId=%s contractDetails=%s' %
                      (reqId, print_IBCpp_contractDetails(contractDetails)))
     aRequest = self._activeRequests.get_by_reqId_otherwise_exit(reqId)
     newRow = pd.DataFrame(
         {
             'right': contractDetails.summary.right,
             'strike': float(contractDetails.summary.strike),
             'expiry': contractDetails.summary.expiry,
             'contractName': print_IBCpp_contract(contractDetails.summary),
             'security': from_contract_to_security(contractDetails.summary),
             'contract': contractDetails.summary,
             'multiplier': contractDetails.summary.multiplier,
             'contractDetails': contractDetails
         },
         index=[len(aRequest.returnedResult)])
     aRequest.returnedResult = aRequest.returnedResult.append(newRow)
Exemple #18
0
    def order_status_monitor(self,
                             ibpyOrderId,
                             target_status,
                             waitingTimeInSeconds=30):
        self._log.debug(
            __name__ +
            '::order_status_monitor: ibpyOrderId=%s target_status=%s' %
            (ibpyOrderId, target_status))
        timer = dt.datetime.now()
        while True:
            time.sleep(
                1
            )  # Do not send too much request to TD max=120 requests/minute
            self._submit_request_after_checking_cache(ReqOneOrder(ibpyOrderId))

            if (dt.datetime.now() -
                    timer).total_seconds() <= waitingTimeInSeconds:
                tmp_status = self.get_order(ibpyOrderId).status
                if isinstance(target_status, str):
                    if tmp_status == target_status:
                        return
                elif isinstance(target_status, list):
                    if tmp_status in target_status:
                        return
            else:
                self._log.error(
                    __name__ +
                    '::order_status_monitor: EXIT, waiting time is too long, >%i'
                    % (waitingTimeInSeconds, ))

                order = self.get_order(ibpyOrderId)
                contract = order.contract
                self._log.error(
                    __name__ +
                    '::order_status_monitor: EXIT, ibpyOrderId=%i status=%s contract=%s'
                    % (ibpyOrderId, order.status,
                       print_IBCpp_contract(contract)))
                raise OrderStatusNotConfirmed()
Exemple #19
0
    def error(self, errorId, errorCode, errorMessage):
        """
        errorId can be either reqId or orderId or -1
        only print real error messages, which is errorId < 2000 in IB's error
        message system, or program is in debug mode
        """
        self._log.debug(__name__ +
                        '::error: errorId=%s errorCode=%s errorMessage=%s' %
                        (errorId, errorCode, errorMessage))
        if errorCode in [501, 326]:  # Do nothing
            # errorCode=501 errorMessage=Already connected.
            # errorCode=326 errorMessage=Unable to connect as the client id is already in use. Retry with a unique client id.
            return
        if errorCode in [502]:
            # errorCode:502 errorId:-1 errorMessage=Couldn't connect to TWS.  Confirm that "Enable ActiveX and Socket Clients" is enabled on the TWS "Configure->API" menu.
            self._log.error('Hint 1: Enable API.')
            self._log.error('Hint 2: Maybe the port number does not match.')
            raise CustomError(
                errorCode,
                'errorId:%s errorMessage=%s' % (errorId, errorMessage))
        if errorCode in [509, 10141]:
            # errorCode:509 errorId:-1 errorMessage=Exception caught while reading socket - Broken pipe
            # errorCode=509 errorMessage=Exception caught while reading socket - Connection reset by peer -- Reason: Invoke connectWrapper() when there is connection already.
            # errorCode=509 errorMessage=Exception caught while reading socket - Connection reset by peer -- Reason 1: IB Gateway was turned off suddenly
            # errorCode:10141 errorId:-1 errorMessage=Paper trading disclaimer must first be accepted for API connection.
            self._log.error(
                __name__ + '::error: errorId=%s errorCode=%s errorMessage=%s' %
                (errorId, errorCode, errorMessage))
            self._log.error(
                'Hint: Restart Trader Workstation(TWS) or IB Gateway and try it again.'
            )
            self._log.error(
                'Hint: IBridgePy Premium members can turn on the auto-connection feature by settings.py --> PROJECT --> autoReconnectPremium --> True'
            )
            self._log.error(
                'Hint: Please refer to YouTube tutorial https://youtu.be/pson8T5ZaRw'
            )
            raise CustomError(
                errorCode,
                'errorId:%s errorMessage=%s' % (errorId, errorMessage))

        elif errorCode in [1100, 1101, 1102]:
            if errorCode == 1100:
                # Your TWS/IB Gateway has been disconnected from IB servers. This can occur because of an
                # internet connectivity issue, a nightly reset of the IB servers, or a competing session.
                self._connectionGatewayToServer = False
            elif errorCode in [1101, 1102]:
                # 1101
                # The TWS/IB Gateway has successfully reconnected to IB's servers. Your market data requests have
                # been lost and need to be re-submitted.
                # 1102
                # The TWS/IB Gateway has successfully reconnected to IB's servers. Your market data requests have been
                # recovered and there is no need for you to re-submit them.
                self._connectionGatewayToServer = True

        elif errorCode in [
                2100, 2103, 2104, 2105, 2106, 2107, 2108, 2110, 2119, 2137,
                2157, 2158
        ]:
            if errorCode in [2103, 2157]:
                # errorCode=2157 errorMessage=Sec-def data farm connection is broken:secdefnj
                self._connectionMarketDataFarm = False
            elif errorCode in [2104, 2108, 2158]:
                # errorCode=2104 errorMessage=Market data farm connection is OK:usfarm.nj
                # errorCode=2158 errorMessage=Sec-def data farm connection is OK:secdefnj
                # errorCode=2108 errorMessage=Market data farm connection is inactive but should be available upon demand.usfarm.nj
                self._connectionMarketDataFarm = True
            elif errorCode == 2105:
                # 2105 = HMDS dataFromServer farm connection is broken:ushmds
                self._connectionHistDataFarm = False
            elif errorCode in [2106, 2107]:
                # errorCode=2106 errorMessage=HMDS data farm connection is OK:ushmds
                self._connectionHistDataFarm = True
            elif errorCode == 2110:
                # Connectivity between TWS and server is broken. It will be restored automatically.
                # noinspection PyAttributeOutsideInit
                self._connectionGatewayToServer = False
                # noinspection PyAttributeOutsideInit
                self._connectionHistDataFarm = False
                # noinspection PyAttributeOutsideInit
                self._connectionMarketDataFarm = False
            elif errorCode == 2137:
                # errorCode=2137 errorMessage=The closing order quantity is greater than your current position.
                self._log.error(
                    __name__ +
                    '::error: errorId=%s errorCode=%s errorMessage=%s' %
                    (errorId, errorCode, errorMessage))
                self._log.error(
                    'errorCode=2137 is not a critical error in IBridgePy anymore. To hide this error, TWS -> Global Configuration -> Messages -> Cross side warning (untick & save) -> Uncheck it.'
                )
            elif errorCode == 2100:
                # errorCode=2100 errorMessage=API client has been unsubscribed from account data.
                self._activeRequests.set_all_requests_of_a_reqType_to_a_status(
                    'reqAccountUpdates', ReqAttr.Status.COMPLETED)
                return
        elif errorCode in [202, 10147, 10148]:
            # 202: cancel order is confirmed
            # 10148, error message: OrderId 230 that needs to be cancelled can not be cancelled, state: Cancelled.
            # 10147, error message: OrderId 2 that needs to be cancelled is not found.
            # errorId is OrderId in this case
            self._activeRequests.set_a_request_of_an_orderId_to_a_status(
                errorId, ReqAttr.Status.COMPLETED)

        elif errorCode in [201, 399, 2113, 2148,
                           2109]:  # No action, just show error message
            # 201 = order rejected - Reason: No such order
            # 201 errorMessage=Order rejected - reason:CASH AVAILABLE: 28328.69; CASH NEEDED FOR THIS ORDER AND OTHER PENDING ORDERS: 56477.64
            # 201 Order rejected -
            # 2109: Order Event Warning: Attribute 'Outside Regular Trading Hours' is ignored based on the order type and destination. PlaceOrder is now being processed.
            self._log.error(__name__ +
                            ':errorId=%s errorCode=%s errorMessage=%s' %
                            (errorId, errorCode, errorMessage))

        elif errorCode == 162:
            if 'API scanner subscription cancelled' in errorMessage:
                self._activeRequests.set_a_request_of_a_reqId_to_a_status(
                    errorId, ReqAttr.Status.COMPLETED)
                return  # not a true error
            else:
                self._log.error(__name__ +
                                ':errorId=%s errorCode=%s errorMessage=%s' %
                                (errorId, errorCode, errorMessage))
                self._print_version()
                raise CustomError(
                    errorCode,
                    'errorId:%s errorMessage=%s' % (errorId, errorMessage))

        elif 110 <= errorCode <= 449:
            # 404: errorMessage=Order held while securities are located = IB doesn't have available shares to borrow and doesn't let short order.
            if errorCode == 165:
                # 165 = Historical Market Data Service query message:10 out of 178 items retrieve
                return
            self._log.error(__name__ +
                            '::error:errorId=%s errorCode=%s errorMessage=%s' %
                            (errorId, errorCode, errorMessage))
            if errorCode == 200:
                # 200 = No security definition has been found for the request.

                # A special case.
                # The connectivity is lost during reqHistoricalData.
                # If get 200 at this moment, it is a fake error.
                if (self._connectionGatewayToServer is
                        False) or (self._connectionHistDataFarm is False):
                    self._log.error(
                        __name__ +
                        '::error: <No security definition has been found for the request> might be a false statement because connectivity to IB server is lost.'
                    )
                    return

                if errorId in self._allRequests.keys():
                    reqId = errorId
                    if 'contract' in self._allRequests[errorId].param:
                        self._log.error(
                            print_IBCpp_contract(
                                self._allRequests[errorId].param['contract']))
                    else:
                        self._log.error(
                            print_IBCpp_contract(
                                self._allRequests[errorId].param['security']))
                else:
                    reqId = self._activeRequests.find_reqId_by_int_orderId(
                        errorId)
                    self._log.error(
                        print_IBCpp_contract(
                            self._allRequests[reqId].param['contract']))

                # if the reqId is an active request, raise error.
                # Otherwise, ignore error because IB may throw the old error a few times.
                if self._activeRequests.has_reqId(reqId):
                    raise CustomError(
                        errorCode,
                        'errorId:%s errorMessage=%s' % (errorId, errorMessage))
                else:
                    return

            # elif errorCode == 165:  # Historical Market Data Service query message:Trading TWS session is connected from a different IP address
            self._print_version()
            raise CustomError(
                errorCode,
                'errorId:%s errorMessage=%s' % (errorId, errorMessage))
        elif 994 <= errorCode <= 999 or 972 <= errorCode <= 978:
            self._log.error(__name__ +
                            ':errorId=%s errorCode=%s errorMessage=%s' %
                            (errorId, errorCode, errorMessage))
            self._log.error(
                'Hint: Please refer to YouTube tutorial https://youtu.be/pson8T5ZaRw'
            )
            self._print_version_and_exit()
        elif 979 <= errorCode <= 988:
            self._log.error(__name__ +
                            ':errorId=%s errorCode=%s errorMessage=%s' %
                            (errorId, errorCode, errorMessage))
            self._log.error(
                'IBridgePy community version supports backtest on US equities only. Please visit https://ibridgepy.com/features-of-ibridgepy/ and consider IBridgePy Backtester version.'
            )
            self._print_version_and_exit()
        else:
            # errorCode=504 errorMessage=Not connected
            self._log.error(
                __name__ + '::error: errorId=%s errorCode=%s errorMessage=%s' %
                (errorId, errorCode, errorMessage))
            self._print_version()
            raise CustomError(
                errorCode,
                'errorId:%s errorMessage=%s' % (errorId, errorMessage))
Exemple #20
0
    def _send_req_to_server(self, activeRequests):
        """
        pandas dataFrame: reqData
        All requests are defined in broker_client_factory::BrokerClientDefs
        """
        self._log.debug(__name__ + '::_send_req_to_server: brokerClient=%s' %
                        (self, ))

        for reqId in activeRequests.get_request_ids():
            aRequest = activeRequests.get_by_reqId_otherwise_exit(reqId)
            self._allRequests[
                reqId] = aRequest  # move to allRequest which stores all requests
            aRequest.status = ReqAttr.Status.SUBMITTED
            reqType = aRequest.reqType
            param = aRequest.param
            self._log.debug('%s' % (aRequest, ))

            if reqType == 'reqPositions':
                self.reqPositionsWrapper()

            elif reqType == 'reqConnect':
                ans = self.isConnectedWrapper()
                if ans:
                    aRequest.status = ReqAttr.Status.COMPLETED
                else:
                    ans = self.connectWrapper()
                    if ans:
                        aRequest.status = ReqAttr.Status.COMPLETED

            elif reqType == 'reqCurrentTime':
                self.reqCurrentTimeWrapper()

            elif reqType == 'reqAllOpenOrders':
                self.reqAllOpenOrdersWrapper()

            elif reqType == 'reqOneOrder':
                ibpyOrderId = param['orderId']
                self.reqOneOrderWrapper(ibpyOrderId)

            elif reqType == 'reqAccountUpdates':
                accountCode = param['accountCode']
                subscribe = param['subscribe']
                self.reqAccountUpdatesWrapper(
                    subscribe, accountCode)  # Request to update account info

            elif reqType == 'reqAccountSummary':
                group = param['group']
                tag = param['tag']
                self.reqAccountSummaryWrapper(reqId, group, tag)

            elif reqType == 'reqIds':
                self.reqIdsWrapper()

            elif reqType == 'reqHeartBeats':
                self.reqHeartBeatsWrapper()

            elif reqType == 'reqHistoricalData':
                security = param['security']
                endTime = param['endTime']
                goBack = param['goBack']
                barSize = param['barSize']
                whatToShow = param['whatToShow']
                useRTH = param['useRTH']
                formatDate = 2  # param['formatDate'] Send epoch to IB server all the time. It is easier to handle
                self.reqHistoricalDataWrapper(
                    reqId, from_security_to_contract(security), endTime,
                    goBack, barSize, whatToShow, useRTH, formatDate)

            elif reqType == 'reqMktData':
                security = param['security']
                genericTickList = param['genericTickList']
                snapshot = param['snapshot']

                # put security and reqID in dictionary for fast access
                # it is keyed by both security and reqId
                self._realTimePriceRequestedList.addReqIdAndStrSecurity(
                    reqId, security.full_print())

                self.reqMktDataWrapper(
                    reqId, from_security_to_contract(security),
                    genericTickList,
                    snapshot)  # Send market data request to IB server

            elif reqType == 'cancelMktData':
                security = param['security']
                reqId = self._realTimePriceRequestedList.findByStrSecurity(
                    security.full_print())
                self.cancelMktDataWrapper(reqId)
                self._realTimePriceRequestedList.deleteReqIdAndStrSecurity(
                    reqId, security.full_print())

            elif reqType == 'reqRealTimeBars':
                security = param['security']
                barSize = param['barSize']
                whatToShow = param['whatToShow']
                useRTH = param['useRTH']
                self._realTimePriceRequestedList.addReqIdAndStrSecurity(
                    reqId, security.full_print())
                self.reqRealTimeBarsWrapper(
                    reqId, from_security_to_contract(security), barSize,
                    whatToShow,
                    useRTH)  # Send market dataFromServer request to IB server

            elif reqType == 'placeOrder':
                """
                Ending label is IBCpp::callBacks::orderStatus
                """
                contract = param['contract']
                order = param['order']
                self.placeOrderWrapper(contract, order, aRequest)

                # When the order is placed, IB will callback orderStatus
                # In broker_client_factory::CallBack::orderStatus, IBridgePy, set ending flag for this request and assign ibpyOrderId
                # self.activeRequests.get_by_reqId_otherwise_exit(reqId).returnedResult = str_ibpyOrderId
                # self.activeRequests.get_by_reqId_otherwise_exit(reqId).status = ReqAttr.Status.COMPLETED

            elif reqType == 'modifyOrder':
                """
                Ending label is IBCpp::callBacks::orderStatus
                """
                ibpyOrderId = param['ibpyOrderId']
                contract = param['contract']
                order = param['order']

                int_orderId = order.orderId
                self._idConverter.verifyRelationship(int_orderId, ibpyOrderId)
                self._log.info(
                    'ModifyOrder ibpyOrderId=%s security=%s order=%s' %
                    (ibpyOrderId, print_IBCpp_contract(contract),
                     print_IBCpp_order(order)))
                self.modifyOrderWrapper(contract, order, aRequest)

            elif reqType == 'reqContractDetails':
                security = param['security']
                self.reqContractDetailsWrapper(
                    reqId, from_security_to_contract(security))

            elif reqType == 'calculateImpliedVolatility':
                security = param['security']
                optionPrice = float(param['optionPrice'])
                underPrice = float(param['underPrice'])

                # put security and reqID in dictionary for fast access
                # it is keyed by both security and reqId
                self._realTimePriceRequestedList.addReqIdAndStrSecurity(
                    reqId, security.full_print())

                self.calculateImpliedVolatilityWrapper(
                    reqId, from_security_to_contract(security), optionPrice,
                    underPrice)

            elif reqType == 'reqScannerSubscription':
                subscription = param['subscription']
                self.reqScannerSubscriptionWrapper(reqId, subscription)

                # Need to upgrade IBCpp
                # tagValueList = self.end_check_list.loc[idx, 'reqData'].param['tagValueList']
                # self.reqScannerSubscription(reqId, subscription, tagValueList)
                # self._log.debug(__name__ + '::_send_req_to_server:reqScannerSubscription:'
                #               + ' subscription=' + subscription.full_print() + ' tagValueList=' + str(tagValueList))

            elif reqType == 'cancelScannerSubscription':
                tickerId = param['tickerId']
                self.cancelScannerSubscriptionWrapper(tickerId)

            elif reqType == 'cancelOrder':
                """
                Ending label is IBCpp::callBacks::error: errorCode = 10148
                Do not use orderStatus callback to follow up on cancelOrder request because the status can be either Canceled Or PendingCancel
                """
                ibpyOrderId = param['ibpyOrderId']
                int_orderId = self._idConverter.fromBrokerToIB(ibpyOrderId)
                param['int_orderId'] = int_orderId
                self.cancelOrderWrapper(ibpyOrderId)

            elif reqType == 'reqScannerParameters':
                self.reqScannerParametersWrapper()
            else:
                self._log.error(
                    __name__ +
                    '::_send_req_to_server: EXIT, cannot handle reqType=%s' %
                    (reqType, ))
                self.end()