Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Feature request: Raise Errors On Calls #686

Open
gdassori opened this issue Jan 28, 2024 · 0 comments
Open

Feature request: Raise Errors On Calls #686

gdassori opened this issue Jan 28, 2024 · 0 comments

Comments

@gdassori
Copy link

Hello. AFAIK Ib Insync methods doesn't raise errors, let me explain better:

A call like this one:

        bars = await self.ib.reqHistoricalDataAsync(
            contract,
            endDateTime=to_date.strftime('%Y%m%d %H:%M:%S UTC'),
            durationStr=f'{int(duration)} S',
            barSizeSetting='5 mins',
            whatToShow='TRADES',
            useRTH=True,
            formatDate=2,
            keepUpToDate=False
        )

would produce, according to my contract and my code, the following log:

Error 162, reqId 65121: Historical Market Data Service error message:No historical market data for EUR/CASH@FXSUBPIP Last 300, contract: Contract(secType='CASH', conId=12087792, symbol='EUR', exchange='IDEALPRO', currency='USD', localSymbol='EUR.USD', tradingClass='EUR.USD')

And an empty "bars" list returned.

To work around this issue (the blindness of a developer on errors) I wrote a small decorator to speculate over the input argument and catch some errors:

def catch_ib_error_on_contract(contract_id_key: str, error_code: int):
    def _raise_ib_error_on_contract(func):
        async def wrapper(*args, **kwargs):
            _errors = []

            def _error_handler(*x):
                _errors.append(x)

            contract_id = kwargs[contract_id_key]
            self = args[0]
            self.ib.errorEvent += _error_handler
            try:
                resp = await func(*args, **kwargs)
                for e in _errors:
                    if int(e[1]) == error_code:
                        if int(e[3].conId) == int(contract_id):
                            raise BrokerResponseErrorException(
                                f'Broker Response Error: {e[2]} - {e[3]}'
                            )
                return resp
            finally:
                self.ib.errorEvent -= _error_handler

        return wrapper
    return _raise_ib_error_on_contract

But is tricky to maintain it and it's definitely smelly.

Also, we can have the requestId as a better hook and be clear on the error source without speculating over the errors array in search of a relevant one.

IMHO the best way to handle this would be using a dedicated object and return, at any request, all the data regarding the response received (an errors field, the request id, the response data which may be empty, and so on), but I think that would be hard to switch all the calls to this new behaviour, so my proposal is just to raise exceptions regarding errors.

Having the request id in your hand would make this easy.

To avoid breaking changes on previously releases, you may add an argument in the IB object constructor to raise or not exceptions on methods, that may have a false default.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant