info.bitrich.xchangestream.okex.OkexStreamingMarketDataService Maven / Gradle / Ivy
The newest version!
package info.bitrich.xchangestream.okex;
import static info.bitrich.xchangestream.okex.OkexStreamingService.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import info.bitrich.xchangestream.core.StreamingMarketDataService;
import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.PublishSubject;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.knowm.xchange.dto.Order;
import org.knowm.xchange.dto.marketdata.*;
import org.knowm.xchange.instrument.Instrument;
import org.knowm.xchange.okex.OkexAdapters;
import org.knowm.xchange.okex.dto.marketdata.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OkexStreamingMarketDataService implements StreamingMarketDataService {
private static final Logger LOG = LoggerFactory.getLogger(OkexStreamingMarketDataService.class);
private final OkexStreamingService service;
private final ObjectMapper mapper = StreamingObjectMapperHelper.getObjectMapper();
private final Map>>
orderBookUpdatesSubscriptions;
public OkexStreamingMarketDataService(OkexStreamingService service) {
this.service = service;
this.orderBookUpdatesSubscriptions = new ConcurrentHashMap<>();
}
private final Map orderBookMap = new HashMap<>();
@Override
public Observable getTicker(Instrument instrument, Object... args) {
String channelUniqueId = TICKERS + OkexAdapters.adaptInstrument(instrument);
return service
.subscribeChannel(channelUniqueId)
.filter(message -> message.has("data"))
.flatMap(
jsonNode -> {
List okexTickers =
mapper.treeToValue(
jsonNode.get("data"),
mapper
.getTypeFactory()
.constructCollectionType(List.class, OkexTicker.class));
return Observable.fromIterable(okexTickers).map(OkexAdapters::adaptTicker);
});
}
@Override
public Observable getTrades(Instrument instrument, Object... args) {
String channelUniqueId = TRADES + OkexAdapters.adaptInstrument(instrument);
return service
.subscribeChannel(channelUniqueId)
.filter(message -> message.has("data"))
.flatMap(
jsonNode -> {
List okexTradeList =
mapper.treeToValue(
jsonNode.get("data"),
mapper.getTypeFactory().constructCollectionType(List.class, OkexTrade.class));
return Observable.fromIterable(
OkexAdapters.adaptTrades(okexTradeList, instrument).getTrades());
});
}
@Override
public Observable getFundingRate(Instrument instrument, Object... args) {
String channelUniqueId = FUNDING_RATE + OkexAdapters.adaptInstrument(instrument);
return service
.subscribeChannel(channelUniqueId)
.filter(message -> message.has("data"))
.map(
jsonNode -> {
List okexFundingRates =
mapper.treeToValue(
jsonNode.get("data"),
mapper
.getTypeFactory()
.constructCollectionType(List.class, OkexFundingRate.class));
return OkexAdapters.adaptFundingRate(okexFundingRates);
});
}
@Override
public Observable getOrderBook(Instrument instrument, Object... args) {
String instId = OkexAdapters.adaptInstrument(instrument);
String channelName = args.length >= 1 ? args[0].toString() : "books";
String channelUniqueId = ORDERBOOK + instId;
return service
.subscribeChannel(channelUniqueId)
.filter(message -> message.has("action"))
.flatMap(
jsonNode -> {
// "books5" channel pushes 5 depth levels every time.
String action =
channelName.equals(ORDERBOOK5) ? "snapshot" : jsonNode.get("action").asText();
List okexOrderbooks =
mapper.treeToValue(
jsonNode.get("data"),
mapper
.getTypeFactory()
.constructCollectionType(List.class, OkexOrderbook.class));
if ("snapshot".equalsIgnoreCase(action)) {
OrderBook orderBook = OkexAdapters.adaptOrderBook(okexOrderbooks, instrument);
orderBookMap.put(instId, orderBook);
return Observable.just(orderBook);
} else if ("update".equalsIgnoreCase(action)) {
OrderBook orderBook = orderBookMap.getOrDefault(instId, null);
if (orderBook == null) {
LOG.error(String.format("Failed to get orderBook, instId=%s.", instId));
return Observable.fromIterable(new LinkedList<>());
}
Date timestamp = new Timestamp(Long.parseLong(okexOrderbooks.get(0).getTs()));
okexOrderbooks
.get(0)
.getAsks()
.forEach(
okexPublicOrder ->
orderBook.update(
OkexAdapters.adaptLimitOrder(
okexPublicOrder, instrument, Order.OrderType.ASK, timestamp)));
okexOrderbooks
.get(0)
.getBids()
.forEach(
okexPublicOrder ->
orderBook.update(
OkexAdapters.adaptLimitOrder(
okexPublicOrder, instrument, Order.OrderType.BID, timestamp)));
if (orderBookUpdatesSubscriptions.get(instrument) != null) {
orderBookUpdatesSubscriptions(
instrument,
okexOrderbooks.get(0).getAsks(),
okexOrderbooks.get(0).getBids(),
timestamp);
}
return Observable.just(orderBook);
} else {
LOG.error(
String.format("Unexpected books action=%s, message=%s", action, jsonNode));
return Observable.fromIterable(new LinkedList<>());
}
});
}
@Override
public Observable> getOrderBookUpdates(
Instrument instrument, Object... args) {
return orderBookUpdatesSubscriptions.computeIfAbsent(instrument, v -> PublishSubject.create());
}
private void orderBookUpdatesSubscriptions(
Instrument instrument, List asks, List bids, Date date) {
List orderBookUpdates = new ArrayList<>();
for (OkexPublicOrder ask : asks) {
OrderBookUpdate o =
new OrderBookUpdate(
Order.OrderType.ASK,
ask.getVolume(),
instrument,
ask.getPrice(),
date,
ask.getVolume());
orderBookUpdates.add(o);
}
for (OkexPublicOrder bid : bids) {
OrderBookUpdate o =
new OrderBookUpdate(
Order.OrderType.BID,
bid.getVolume(),
instrument,
bid.getPrice(),
date,
bid.getVolume());
orderBookUpdates.add(o);
}
orderBookUpdatesSubscriptions.get(instrument).onNext(orderBookUpdates);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy