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

info.bitrich.xchangestream.poloniex2.PoloniexStreamingService Maven / Gradle / Ivy

package info.bitrich.xchangestream.poloniex2;

import com.fasterxml.jackson.databind.JsonNode;
import info.bitrich.xchangestream.poloniex2.dto.PoloniexWebSocketEvent;
import info.bitrich.xchangestream.poloniex2.dto.PoloniexWebSocketEventsTransaction;
import info.bitrich.xchangestream.poloniex2.dto.PoloniexWebSocketOrderbookModifiedEvent;
import info.bitrich.xchangestream.poloniex2.dto.PoloniexWebSocketSubscriptionMessage;
import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService;
import io.reactivex.rxjava3.core.Observable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.knowm.xchange.currency.CurrencyPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Created by Lukas Zaoralek on 10.11.17. */
public class PoloniexStreamingService extends JsonNettyStreamingService {
  private static final Logger LOG = LoggerFactory.getLogger(PoloniexStreamingService.class);

  private static final String HEARTBEAT = "1010";

  private final Map subscribedChannels = new ConcurrentHashMap<>();
  private final Map> subscriptions = new ConcurrentHashMap<>();

  public PoloniexStreamingService(String apiUrl) {
    super(apiUrl, Integer.MAX_VALUE, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_RETRY_DURATION, 2);
  }

  @Override
  protected void handleMessage(JsonNode message) {

    if (message.isArray()) {
      if (message.size() < 3) {
        if (message.get(0).asText().equals(HEARTBEAT)) return;
        else if ("1002".equals(message.get(0).asText())) return;
      }
      int channelId = Integer.parseInt(message.get(0).toString());
      if (channelId > 0 && channelId < 1000) {
        JsonNode events = message.get(2);
        if (events != null && events.isArray()) {
          JsonNode event = events.get(0);
          if (event.get(0).toString().equals("\"i\"")) {
            if (event.get(1).has("orderBook")) {
              subscribedChannels.compute(
                  String.valueOf(channelId),
                  (key, oldValue) -> {
                    String currencyPair = event.get(1).get("currencyPair").asText();
                    if (oldValue != null && !oldValue.equals(currencyPair)) {
                      throw new RuntimeException("Attempted currency pair channel id reassignment");
                    }
                    if (oldValue == null) {
                      LOG.info("Register {} as {}", channelId, currencyPair);
                    } else {
                      LOG.debug("Order book reinitialization {} {}", channelId, currencyPair);
                    }
                    return currencyPair;
                  });
            }
          }
        }
      }
    }
    if (message.has("error")) {
      LOG.error("Error with message: " + message.get("error").asText());
      return;
    }
    super.handleMessage(message);
  }

  @Override
  public boolean processArrayMessageSeparately() {
    return false;
  }

  @Override
  public synchronized Observable subscribeChannel(String channelName, Object... args) {
    if (!channels.containsKey(channelName)) {
      subscriptions.put(channelName, super.subscribeChannel(channelName, args));
    }
    return subscriptions.get(channelName);
  }

  public Observable> subscribeCurrencyPairChannel(
      CurrencyPair currencyPair) {
    String channelName = currencyPair.counter.toString() + "_" + currencyPair.base.toString();
    return subscribeChannel(channelName)
        .map(
            jsonNode ->
                objectMapper.treeToValue(jsonNode, PoloniexWebSocketEventsTransaction.class))
        .scan(
            (poloniexWebSocketEventsTransactionOld, poloniexWebSocketEventsTransactionNew) -> {
              final boolean initialSnapshot =
                  poloniexWebSocketEventsTransactionNew.getEvents().stream()
                      .anyMatch(PoloniexWebSocketOrderbookModifiedEvent.class::isInstance);
              final boolean sequenceContinuous =
                  poloniexWebSocketEventsTransactionOld.getSeqId() + 1
                      == poloniexWebSocketEventsTransactionNew.getSeqId();
              if (!initialSnapshot || sequenceContinuous) {
                return poloniexWebSocketEventsTransactionNew;
              } else {
                throw new RuntimeException(
                    String.format(
                        "Invalid sequencing, old: %s new: %s",
                        objectMapper.writeValueAsString(poloniexWebSocketEventsTransactionOld),
                        objectMapper.writeValueAsString(poloniexWebSocketEventsTransactionNew)));
              }
            })
        .map(PoloniexWebSocketEventsTransaction::getEvents)
        .share();
  }

  @Override
  protected String getChannelNameFromMessage(JsonNode message) {
    String strChannelId = message.get(0).asText();
    int channelId = Integer.parseInt(strChannelId);
    if (channelId >= 1000) return strChannelId;
    else return subscribedChannels.get(message.get(0).asText());
  }

  @Override
  public String getSubscribeMessage(String channelName, Object... args) throws IOException {
    PoloniexWebSocketSubscriptionMessage subscribeMessage =
        new PoloniexWebSocketSubscriptionMessage("subscribe", channelName);
    return objectMapper.writeValueAsString(subscribeMessage);
  }

  @Override
  public String getUnsubscribeMessage(String channelName, Object... args) throws IOException {
    PoloniexWebSocketSubscriptionMessage subscribeMessage =
        new PoloniexWebSocketSubscriptionMessage("unsubscribe", channelName);
    return objectMapper.writeValueAsString(subscribeMessage);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy