pykrakenapi icon indicating copy to clipboard operation
pykrakenapi copied to clipboard

Add exception_handler_hook optional arg

Open puntonim opened this issue 3 years ago • 4 comments

This code change adds a new feature: a custom exception handler hook that, when provided, is executed to handle error responses from Kraken. This code change is fully backward compatible.

Current scenario

All error responses from Kraken are re-tried. But in some cases, re-trying does not make sense, fi. when the transaction id does not exist. An actual example: querying order info for a non-existing transaction id, results in many attempts, as the code fails at recognizing the error message received from Kraken:

api = KrakenAPI()
txid = "O6LXLS-NSMIX-4AL459"  # A non-existing transaction id.
api.query_orders_info(txid, trades=True)

Results in:

attempt: 000 | ['EOrder:Invalid order']
attempt: 001 | ['EOrder:Invalid order']
attempt: 002 | ['EOrder:Invalid order']
attempt: 003 | ['EOrder:Invalid order']
attempt: 004 | ['EOrder:Invalid order']
attempt: 005 | ['EOrder:Invalid order']
attempt: 006 | ['EOrder:Invalid order']
attempt: 007 | ['EOrder:Invalid order']
attempt: 008 | ['EOrder:Invalid order']
...

Scenario with this code change

Whit this PR instead I can optionally define a custom exception handler hook. And this hook can recognize an error response from Kraken and raise, avoiding the unnecessary attempts and instead raising a proper exception. Example code (code that uses pykrakenapi as requirement):

# Custom exception that identifies a specific error response from Kraken.
class ErrorResponseInvalidOrder(BaseErrorResponse):
    def __init__(self, txid):
        self.txid = txid

# Custom exception handler hook.
def exception_handler_hook(exc, call_args, call_kwargs):
    if "EOrder:Invalid order" in exc.args[0]:
        raise ErrorResponseInvalidOrder(txid=call_args[1]) from exc


api = KrakenAPI(
    ...
    exception_handler_hook=exception_handler_hook,
)

# Finally when I call `query_orders_info` I can handle the case when the transaction id does not exist.
txid = "O6LXLS-NSMIX-4AL459"  # A non-existing trannsaction id.
try:
    response = api.query_orders_info(txid, trades=True)
except exceptions.ErrorResponseInvalidOrder as exc:
    print(f"Order not found: txid={exc.txid}")

Finally, this code change is backward compatible, so the existing users of this library do NOT have to change their code (unless they want to use this new feature indeed 😉 ).

puntonim avatar Aug 14 '21 15:08 puntonim

@dominiktraxl Hi, any chance to take a look at this? 😝

puntonim avatar Aug 28 '21 04:08 puntonim

Hi @puntonim, thank you for the contribution. Looks like a useful feature, and I'm fine with the implementation as well. I just have one request: could you please expand a little on the example in the docstring? The example as it is now prints the error, which is already done when an exception is caught. Maybe you could use the example you describe here, as this is a practical use case.

dominiktraxl avatar Aug 30 '21 09:08 dominiktraxl

Hi @puntonim, thank you for the contribution. Looks like a useful feature, and I'm fine with the implementation as well. I just have one request: could you please expand a little on the example in the docstring? The example as it is now prints the error, which is already done when an exception is caught. Maybe you could use the example you describe here, as this is a practical use case.

Good point, I'll work on that!

puntonim avatar Aug 30 '21 09:08 puntonim

@puntonim are you planning on updating this? I'm getting a similar issue so would be great if you could update this?

In my scenario, I'm getting locked into the following loop since the error is not being handled:

attempt: 000 | ['EFunding:Invalid amount']
attempt: 001 | ['EFunding:Invalid amount']
attempt: 002 | ['EFunding:Invalid amount']
attempt: 003 | ['EFunding:Invalid amount']
call rate limiter exceeded (counter=20, limit=20) 
 sleeping for 5 seconds

For me, I'd be good with just printing the error and returning it.

jonnymaserati avatar Apr 01 '22 21:04 jonnymaserati