All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.bitrich.xchangestream.kraken.KrakenStreamingTradeService Maven / Gradle / Ivy

There is a newer version: 5.2.1
Show newest version
package info.bitrich.xchangestream.kraken;

import com.fasterxml.jackson.databind.JsonNode;
import info.bitrich.xchangestream.core.StreamingTradeService;
import info.bitrich.xchangestream.kraken.dto.KrakenOpenOrder;
import info.bitrich.xchangestream.kraken.dto.KrakenOwnTrade;
import info.bitrich.xchangestream.kraken.dto.enums.KrakenSubscriptionName;
import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper;
import io.reactivex.Observable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.Order;
import org.knowm.xchange.dto.trade.LimitOrder;
import org.knowm.xchange.dto.trade.MarketOrder;
import org.knowm.xchange.dto.trade.StopOrder;
import org.knowm.xchange.dto.trade.UserTrade;
import org.knowm.xchange.kraken.KrakenAdapters;
import org.knowm.xchange.kraken.dto.trade.KrakenOrderFlags;
import org.knowm.xchange.kraken.dto.trade.KrakenOrderStatus;
import org.knowm.xchange.kraken.dto.trade.KrakenType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class KrakenStreamingTradeService implements StreamingTradeService {
  private static final Logger LOG = LoggerFactory.getLogger(KrakenStreamingTradeService.class);

  private final KrakenStreamingService streamingService;

  private volatile boolean ownTradesObservableSet, userTradeObservableSet;
  private Observable ownTradesObservable;
  private Observable userTradeObservable;

  KrakenStreamingTradeService(KrakenStreamingService streamingService) {
    this.streamingService = streamingService;

    if (streamingService != null) {
      streamingService.subscribeDisconnect().subscribe(o -> {
        synchronized (this) {
          ownTradesObservableSet = false;
          userTradeObservableSet = false;
        }
      });
    }
  }

  private String getChannelName(KrakenSubscriptionName subscriptionName) {
    return subscriptionName.toString();
  }

  private static class KrakenDtoOrderHolder extends HashMap {}

  private static class KrakenDtoUserTradeHolder extends HashMap {}

  @Override
  public Observable getOrderChanges(CurrencyPair currencyPair, Object... args) {
    try {
      if (!ownTradesObservableSet) {
        synchronized (this) {
          if (!ownTradesObservableSet) {
            String channelName = getChannelName(KrakenSubscriptionName.openOrders);
            ownTradesObservable =
                streamingService
                    .subscribeChannel(channelName)
                    .filter(JsonNode::isArray)
                    .filter(Objects::nonNull)
                    .map(jsonNode -> jsonNode.get(0))
                    .map(
                        jsonNode ->
                            StreamingObjectMapperHelper.getObjectMapper()
                                .treeToValue(jsonNode, KrakenDtoOrderHolder[].class))
                    .flatMapIterable(this::adaptKrakenOrders)
                    .share();

            ownTradesObservableSet = true;
          }
        }
      }
      return Observable.create(
          emit ->
              ownTradesObservable
                  .filter(
                      order ->
                          currencyPair == null
                              || order.getCurrencyPair() == null
                              || order.getCurrencyPair().compareTo(currencyPair) == 0)
                  .subscribe(emit::onNext, emit::onError, emit::onComplete));

    } catch (Exception e) {
      return Observable.error(e);
    }
  }

  private Iterable adaptKrakenOrders(KrakenDtoOrderHolder[] dtoList) {
    List result = new ArrayList<>();

    for (KrakenDtoOrderHolder dtoHolder : dtoList) {
      for (Map.Entry dtoOrderEntry : dtoHolder.entrySet()) {
        String orderId = dtoOrderEntry.getKey();
        KrakenOpenOrder dto = dtoOrderEntry.getValue();
        KrakenOpenOrder.KrakenDtoDescr descr = dto.descr;

        CurrencyPair pair = descr == null ? null : new CurrencyPair(descr.pair);
        Order.OrderType side =
            descr == null ? null : KrakenAdapters.adaptOrderType(KrakenType.fromString(descr.type));
        String orderType = (descr == null || descr.ordertype == null) ? null : descr.ordertype;

        Order.Builder builder;
        if ("limit".equals(orderType))
          builder = new LimitOrder.Builder(side, pair).limitPrice(descr.price);
        else if ("stop".equals(orderType))
          builder =
              new StopOrder.Builder(side, pair).limitPrice(descr.price).stopPrice(descr.price2);
        else if ("market".equals(orderType)) builder = new MarketOrder.Builder(side, pair);
        else // this is an order update (not the full order, it may only update one field)
        builder = new MarketOrder.Builder(side, pair);

        result.add(
            builder
                .id(orderId)
                .originalAmount(dto.vol)
                .cumulativeAmount(dto.vol_exec)
                .averagePrice(dto.avg_price)
                .orderStatus(
                    dto.status == null
                        ? null
                        : KrakenAdapters.adaptOrderStatus(KrakenOrderStatus.fromString(dto.status)))
                .timestamp(dto.opentm == null ? null : new Date((long) (dto.opentm * 1000L)))
                .fee(dto.fee)
                .flags(adaptFlags(dto.oflags))
                .userReference(dto.userref == null ? null : Integer.toString(dto.userref))
                .build());
      }
    }
    return result;
  }

  /**
   * Comma delimited list of order flags (optional). viqc = volume in quote currency (not available
   * for leveraged orders), fcib = prefer fee in base currency, fciq = prefer fee in quote currency,
   * nompp = no market price protection, post = post only order (available when ordertype = limit)
   */
  private Set adaptFlags(String oflags) {
    if (oflags == null) return new HashSet<>(0);

    String[] arr = oflags.split(",");
    Set flags = new HashSet<>(arr.length);

    for (String flag : arr) {
      flags.add(KrakenOrderFlags.fromString(flag));
    }
    return flags;
  }

  @Override
  public Observable getUserTrades(CurrencyPair currencyPair, Object... args) {
    try {
      if (!userTradeObservableSet) {
        synchronized (this) {
          if (!userTradeObservableSet) {
            String channelName = getChannelName(KrakenSubscriptionName.ownTrades);
            userTradeObservable =
                streamingService
                    .subscribeChannel(channelName)
                    .filter(JsonNode::isArray)
                    .filter(Objects::nonNull)
                    .map(jsonNode -> jsonNode.get(0))
                    .map(
                        jsonNode ->
                            StreamingObjectMapperHelper.getObjectMapper()
                                .treeToValue(jsonNode, KrakenDtoUserTradeHolder[].class))
                    .flatMapIterable(this::adaptKrakenUserTrade)
                    .share();
            userTradeObservableSet = true;
          }
        }
      }
      return Observable.create(
          emit ->
              userTradeObservable
                  .filter(
                      order ->
                          currencyPair == null
                              || order.getCurrencyPair() == null
                              || order.getCurrencyPair().compareTo(currencyPair) == 0)
                  .subscribe(emit::onNext, emit::onError, emit::onComplete));

    } catch (Exception e) {
      return Observable.error(e);
    }
  }

  private List adaptKrakenUserTrade(KrakenDtoUserTradeHolder[] ownTrades) {
    List result = new ArrayList<>();

    for (KrakenDtoUserTradeHolder holder : ownTrades) {
      for (Map.Entry entry : holder.entrySet()) {
        String tradeId = entry.getKey();
        KrakenOwnTrade dto = entry.getValue();

        CurrencyPair currencyPair = new CurrencyPair(dto.pair);
        result.add(
            new UserTrade.Builder()
                .id(tradeId) // The tradeId should be the key of the map, postxid can be null and is
                // not unique as required for a tradeId
                .orderId(dto.ordertxid)
                .currencyPair(currencyPair)
                .timestamp(dto.time == null ? null : new Date((long) (dto.time * 1000L)))
                .type(KrakenAdapters.adaptOrderType(KrakenType.fromString(dto.type)))
                .price(dto.price)
                .feeAmount(dto.fee)
                .feeCurrency(currencyPair.counter)
                .originalAmount(dto.vol)
                .build());
      }
    }
    return result;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy