pykrakenapi
pykrakenapi copied to clipboard
Add exception_handler_hook optional arg
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 😉 ).
@dominiktraxl Hi, any chance to take a look at this? 😝
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.
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 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.