Exchange-core is an open source market exchange core based on LMAX Disruptor, Eclipse Collections (ex. Goldman Sachs GS Collections), Real Logic Agrona, OpenHFT Chronicle-Wire, LZ4 Java, and Adaptive Radix Trees.
Exchange-core includes:
Designed for high scalability and pauseless 24/7 operation under high-load conditions and providing low-latency responses:
Single order book configuration is capable to process 5M operations per second on 10-years old hardware (Intel® Xeon® X5690) with moderate latency degradation:
rate | 50.0% | 90.0% | 95.0% | 99.0% | 99.9% | 99.99% | worst |
---|---|---|---|---|---|---|---|
125K | 0.6µs | 0.9µs | 1.0µs | 1.4µs | 4µs | 24µs | 41µs |
250K | 0.6µs | 0.9µs | 1.0µs | 1.4µs | 9µs | 27µs | 41µs |
500K | 0.6µs | 0.9µs | 1.0µs | 1.6µs | 14µs | 29µs | 42µs |
1M | 0.5µs | 0.9µs | 1.2µs | 4µs | 22µs | 31µs | 45µs |
2M | 0.5µs | 1.2µs | 3.9µs | 10µs | 30µs | 39µs | 60µs |
3M | 0.7µs | 3.6µs | 6.2µs | 15µs | 36µs | 45µs | 60µs |
4M | 1.0µs | 6.0µs | 9µs | 25µs | 45µs | 55µs | 70µs |
5M | 1.5µs | 9.5µs | 16µs | 42µs | 150µs | 170µs | 190µs |
6M | 5µs | 30µs | 45µs | 300µs | 500µs | 520µs | 540µs |
7M | 60µs | 1.3ms | 1.5ms | 1.8ms | 1.9ms | 1.9ms | 1.9ms |
Benchmark configuration:
mvn install
pom.xml
:<dependency>
<groupId>exchange.core2</groupId>
<artifactId>exchange-core</artifactId>
<version>0.5.3</version>
</dependency>
Alternatively, you can clone this repository and run the example test.
Create and start empty exchange core:
// simple async events handler
SimpleEventsProcessor eventsProcessor = new SimpleEventsProcessor(new IEventsHandler() {
@Override
public void tradeEvent(TradeEvent tradeEvent) {
System.out.println("Trade event: " + tradeEvent);
}
@Override
public void reduceEvent(ReduceEvent reduceEvent) {
System.out.println("Reduce event: " + reduceEvent);
}
@Override
public void rejectEvent(RejectEvent rejectEvent) {
System.out.println("Reject event: " + rejectEvent);
}
@Override
public void commandResult(ApiCommandResult commandResult) {
System.out.println("Command result: " + commandResult);
}
@Override
public void orderBook(OrderBook orderBook) {
System.out.println("OrderBook event: " + orderBook);
}
});
// default exchange configuration
ExchangeConfiguration conf = ExchangeConfiguration.defaultBuilder().build();
// no serialization
Supplier<ISerializationProcessor> serializationProcessorFactory = () -> DummySerializationProcessor.INSTANCE;
// build exchange core
ExchangeCore exchangeCore = ExchangeCore.builder()
.resultsConsumer(eventsProcessor)
.serializationProcessorFactory(serializationProcessorFactory)
.exchangeConfiguration(conf)
.build();
// start up disruptor threads
exchangeCore.startup();
// get exchange API for publishing commands
ExchangeApi api = exchangeCore.getApi();
Create new symbol:
// currency code constants
final int currencyCodeXbt = 11;
final int currencyCodeLtc = 15;
// symbol constants
final int symbolXbtLtc = 241;
// create symbol specification and publish it
CoreSymbolSpecification symbolSpecXbtLtc = CoreSymbolSpecification.builder()
.symbolId(symbolXbtLtc) // symbol id
.type(SymbolType.CURRENCY_EXCHANGE_PAIR)
.baseCurrency(currencyCodeXbt) // base = satoshi (1E-8)
.quoteCurrency(currencyCodeLtc) // quote = litoshi (1E-8)
.baseScaleK(1_000_000L) // 1 lot = 1M satoshi (0.01 BTC)
.quoteScaleK(10_000L) // 1 price step = 10K litoshi
.takerFee(1900L) // taker fee 1900 litoshi per 1 lot
.makerFee(700L) // maker fee 700 litoshi per 1 lot
.build();
future = api.submitBinaryDataAsync(new BatchAddSymbolsCommand(symbolSpecXbtLtc));
Create new users:
// create user uid=301
future = api.submitCommandAsync(ApiAddUser.builder()
.uid(301L)
.build());
// create user uid=302
future = api.submitCommandAsync(ApiAddUser.builder()
.uid(302L)
.build());
Perform deposits:
// first user deposits 20 LTC
future = api.submitCommandAsync(ApiAdjustUserBalance.builder()
.uid(301L)
.currency(currencyCodeLtc)
.amount(2_000_000_000L)
.transactionId(1L)
.build());
// second user deposits 0.10 BTC
future = api.submitCommandAsync(ApiAdjustUserBalance.builder()
.uid(302L)
.currency(currencyCodeXbt)
.amount(10_000_000L)
.transactionId(2L)
.build());
Place orders:
// first user places Good-till-Cancel Bid order
// he assumes BTCLTC exchange rate 154 LTC for 1 BTC
// bid price for 1 lot (0.01BTC) is 1.54 LTC => 1_5400_0000 litoshi => 10K * 15_400 (in price steps)
future = api.submitCommandAsync(ApiPlaceOrder.builder()
.uid(301L)
.orderId(5001L)
.price(15_400L)
.reservePrice(15_600L) // can move bid order up to the 1.56 LTC, without replacing it
.size(12L) // order size is 12 lots
.action(OrderAction.BID)
.orderType(OrderType.GTC) // Good-till-Cancel
.symbol(symbolXbtLtc)
.build());
// second user places Immediate-or-Cancel Ask (Sell) order
// he assumes wost rate to sell 152.5 LTC for 1 BTC
future = api.submitCommandAsync(ApiPlaceOrder.builder()
.uid(302L)
.orderId(5002L)
.price(15_250L)
.size(10L) // order size is 10 lots
.action(OrderAction.ASK)
.orderType(OrderType.IOC) // Immediate-or-Cancel
.symbol(symbolXbtLtc)
.build());
Request order book:
future = api.requestOrderBookAsync(symbolXbtLtc, 10);
GtC orders manipulations:
// first user moves remaining order to price 1.53 LTC
future = api.submitCommandAsync(ApiMoveOrder.builder()
.uid(301L)
.orderId(5001L)
.newPrice(15_300L)
.symbol(symbolXbtLtc)
.build());
// first user cancel remaining order
future = api.submitCommandAsync(ApiCancelOrder.builder()
.uid(301L)
.orderId(5001L)
.symbol(symbolXbtLtc)
.build());
Check user balance and GtC orders:
Future<SingleUserReportResult> report = api.processReport(new SingleUserReportQuery(301), 0);
Check system balance:
// check fees collected
Future<TotalCurrencyBalanceReportResult> totalsReport = api.processReport(new TotalCurrencyBalanceReportQuery(), 0);
System.out.println("LTC fees collected: " + totalsReport.get().getFees().get(currencyCodeLtc));
Exchange-core is an open-source project and contributions are welcome!