Context, Logging & Retries
TL;DR
The SDK uses a middleware-based system. You can use this to retry on failure, log calls, or add your own middleware.
Introductory Example
The SDK's methods run plainly by default. But you can easily wrap them to add exponential backoff retries (and more):
from tribulnation.sdk import EarnSDK, Context, NetworkError, RateLimited
earn = EarnSDK()
with Context().retried(NetworkError, RateLimited, max_retries=5).use():
instruments = await earn.instruments(tags=['flexible'])
print('OK')
# Retry 1 for instruments after NetworkError('No network connection'); sleeping 1.00s
# Retry 2 for instruments after NetworkError('No network connection'); sleeping 2.00s
# OKWhat else can it do?
You can also add logging, or you own middleware. Let's see logging first:
from tribulnation.sdk import MarketSDK, Context, NetworkError, RateLimited
market = MarketSDK()
with Context().logged().use():
tickers = await market.tickers('mexc:spot')
# Calling "tickers" with args: ('mexc:spot',), kwargs: {}
# Calling "tickers -> exchange" with args: ('mexc:spot',), kwargs: {}
# Calling "tickers -> exchange -> venue" with args: ('mexc',), kwargs: {}
# Calling "tickers -> exchange -> exchange" with args: ('spot',), kwargs: {}
# Calling "tickers -> tickers" with args: (), kwargs: {'markets': None, 'settings': {}}And now you know what's going on under the hood! Let's explore it further.
How it works
All external methods in the SDK (Market.place_order, Earn.instruments, etc.) are decorated with @SDK.method. The decorator checks for an active Context and, if found, applies its middleware to the call.
Therefore, it's not only the method you call: any delegated calls made inside it will also have the same context applied. If there's a long method hitting rate-limits, do not worry!: the inner calls will be retried, not just the whole thing.
Resource acquisition and cleanup
Context.retried() does not retry SDK context-manager entry or exit. Venues own
those policies and translate client acquisition and cleanup failures into SDK errors.
Current venue resource policies translate errors without automatically retrying them.
Decorated SDK methods called during acquisition or cleanup still receive the active
context's retries and logging. Entry and exit do not produce their own logging spans.
Writing your own middleware (unstable)
A middleware is a callable with this signature:
def middleware(self, fn: Fn, ctx: Context) -> Fn:
...Here Fn is a callable type and Context.path: tuple[str, ...] is a tuple of the method names that have been called to reach this point. This API is yet unstable and may change in the future. Feel free to reach out/open an issue to discuss improvements.