Skip to content

Implementing resiliency features

Adam Walczak edited this page Apr 27, 2020 · 1 revision

NOTE: documentation of an upcoming feature https://github.com/knowm/XChange/pull/3231

This page describes the steps required to implement resiliency features which can be enabled via ExchangeSpecification#getResilience like retries and rate limiting.

Step 1

Add resiliency registries to your exchange class:

public final class BinanceResilience {

  public static final String REQUEST_WEIGHT_RATE_LIMITER = "requestWeight";

  // ...

  private BinanceResilience() {}

  public static ResilienceRegistries createRegistries() {
    ResilienceRegistries registries = new ResilienceRegistries();
    registries
        .rateLimiters()
        .rateLimiter(
            REQUEST_WEIGHT_RATE_LIMITER,
            RateLimiterConfig.from(registries.rateLimiters().getDefaultConfig())
                .limitRefreshPeriod(Duration.ofMinutes(1))
                .limitForPeriod(1200)
                .build());
    
    // ...

    return registries;
  }
}
public class BinanceExchange extends BaseExchange {

  private static ResilienceRegistries RESILIENCE_REGISTRIES;

  // ...

  @Override
  public ResilienceRegistries getResilienceRegistries() {
    if (RESILIENCE_REGISTRIES == null) {
      RESILIENCE_REGISTRIES = BinanceResilience.createRegistries();
    }
    return RESILIENCE_REGISTRIES;
  }

Step 2

Extend BaseResilientExchangeService in your service class and pass the resilience registries to them:

public class BinanceBaseService extends BaseResilientExchangeService<BinanceExchange> {

  // ...

  protected BinanceBaseService(
      BinanceExchange exchange,
      BinanceAuthenticated binance,
      ResilienceRegistries resilienceRegistries) {

    super(exchange, resilienceRegistries);
public class BinanceExchange extends BaseExchange {

  // ...

  @Override
  protected void initServices() {

    // ...

    this.marketDataService = new BinanceMarketDataService(this, binance, getResilienceRegistries());
    this.tradeService = new BinanceTradeService(this, binance, getResilienceRegistries());
    this.accountService = new BinanceAccountService(this, binance, getResilienceRegistries());
  }

Step 4

Use BaseResilientExchangeService#decorateApiCall method for your API calls:

public class BinanceTradeServiceRaw extends BinanceBaseService {

  // ...

  public List<BinanceOrder> openOrders(CurrencyPair pair) throws BinanceException, IOException {
    return decorateApiCall(
            () ->
                binance.openOrders(
                    Optional.ofNullable(pair).map(BinanceAdapters::toSymbol).orElse(null),
                    getRecvWindow(),
                    getTimestampFactory(),
                    apiKey,
                    signatureCreator))
        .withRetry(retry("openOrders"))
        .withRateLimiter(rateLimiter(REQUEST_WEIGHT_RATE_LIMITER), openOrdersPermits(pair))
        .call();
  }

  public BinanceNewOrder newOrder(
      CurrencyPair pair,
      OrderSide side,
      OrderType type,
      TimeInForce timeInForce,
      BigDecimal quantity,
      BigDecimal price,
      String newClientOrderId,
      BigDecimal stopPrice,
      BigDecimal icebergQty)
      throws IOException, BinanceException {
    return decorateApiCall(
            () ->
                binance.newOrder(
                    BinanceAdapters.toSymbol(pair),
                    side,
                    type,
                    timeInForce,
                    quantity,
                    price,
                    newClientOrderId,
                    stopPrice,
                    icebergQty,
                    getRecvWindow(),
                    getTimestampFactory(),
                    apiKey,
                    signatureCreator))
        .withRetry(retry("newOrder", NON_IDEMPOTENTE_CALLS_RETRY_CONFIG_NAME))
        .withRateLimiter(rateLimiter(ORDERS_PER_SECOND_RATE_LIMITER))
        .withRateLimiter(rateLimiter(ORDERS_PER_DAY_RATE_LIMITER))
        .withRateLimiter(rateLimiter(REQUEST_WEIGHT_RATE_LIMITER))
        .call();
  }