CCXT
Java Examples

LoomConcurrencyBench

LoomConcurrencyBench — CCXT Java code example.

package examples;

import io.github.ccxt.exchanges.Binance;
import io.github.ccxt.types.Ticker;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;

/**
 * Concurrency benchmark: N concurrent fetchTicker() calls against a fake exchange
 * whose transport sleeps 200 ms. Ideal wall-clock is ~200 ms regardless of N.
 *
 * Usage:
 *   cd java && ./gradlew :examples:run -PmainClass=examples.LoomConcurrencyBench
 *   cd java && ./gradlew :examples:run -PmainClass=examples.LoomConcurrencyBench -Pargs="64 256"
 */
public class LoomConcurrencyBench {

    static final long LATENCY_MS = 200;

    /** Binance with the HTTP layer replaced by a 200 ms sleep returning a canned ticker. */
    static class FakeExchange extends Binance {
        FakeExchange() {
            super();
            this.enableRateLimit = false;
        }

        @Override
        public CompletableFuture<Object> fetch(Object url, Object method, Object headers, Object body) {
            try {
                Thread.sleep(LATENCY_MS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            Map<String, Object> ticker = new HashMap<>();
            ticker.put("symbol", "BTCUSDT");
            ticker.put("lastPrice", "50000.0");
            ticker.put("bidPrice", "49999.0");
            ticker.put("askPrice", "50001.0");
            ticker.put("highPrice", "51000.0");
            ticker.put("lowPrice", "49000.0");
            ticker.put("volume", "1000.0");
            ticker.put("quoteVolume", "50000000.0");
            ticker.put("closeTime", System.currentTimeMillis());
            return CompletableFuture.completedFuture(ticker);
        }
    }

    static Map<String, Object> fakeMarket() {
        Map<String, Object> m = new HashMap<>();
        m.put("id", "BTCUSDT");
        m.put("symbol", "BTC/USDT");
        m.put("base", "BTC");
        m.put("quote", "USDT");
        m.put("baseId", "BTC");
        m.put("quoteId", "USDT");
        m.put("type", "spot");
        m.put("spot", true);
        m.put("margin", false);
        m.put("swap", false);
        m.put("future", false);
        m.put("option", false);
        m.put("active", true);
        m.put("contract", false);
        m.put("linear", null);
        m.put("inverse", null);
        m.put("precision", new HashMap<String, Object>() {{ put("amount", 1e-8); put("price", 1e-8); }});
        m.put("limits", new HashMap<String, Object>());
        m.put("info", new HashMap<String, Object>());
        return m;
    }

    static long run(FakeExchange ex, int n) {
        long t0 = System.nanoTime();
        List<CompletableFuture<Ticker>> futures = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            futures.add(ex.fetchTickerAsync("BTC/USDT"));
        }
        for (CompletableFuture<Ticker> f : futures) {
            Ticker t = f.join();
            if (t.last == null) {
                throw new IllegalStateException("fetchTicker returned no last price");
            }
        }
        return (System.nanoTime() - t0) / 1_000_000;
    }

    public static void main(String[] args) {
        int[] sizes = args.length > 0
                ? Arrays.stream(args).mapToInt(Integer::parseInt).toArray()
                : new int[]{64, 256};

        FakeExchange ex = new FakeExchange();
        ex.setMarkets(new ArrayList<>(List.of(fakeMarket())));

        System.out.println("commonPool parallelism=" + ForkJoinPool.commonPool().getParallelism()
                + "  latency_each=" + LATENCY_MS + "ms  ideal=~" + LATENCY_MS + "ms");
        run(ex, 4); // warm-up

        for (int n : sizes) {
            long ms = run(ex, n);
            System.out.println("N=" + n + "  fetchTicker wall-clock: " + ms + " ms");
        }
    }
}