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

info.bitrich.xchangestream.bitfinex.BitfinexStreamingAdapters Maven / Gradle / Ivy

package info.bitrich.xchangestream.bitfinex;

import static java.util.stream.StreamSupport.stream;
import static org.knowm.xchange.dto.Order.OrderType.ASK;
import static org.knowm.xchange.dto.Order.OrderType.BID;

import com.fasterxml.jackson.databind.JsonNode;
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthBalance;
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthOrder;
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthPreTrade;
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthTrade;
import io.reactivex.annotations.Nullable;
import java.math.BigDecimal;
import java.util.stream.Stream;
import org.knowm.xchange.bitfinex.service.BitfinexAdapters;
import org.knowm.xchange.bitfinex.v1.BitfinexOrderType;
import org.knowm.xchange.bitfinex.v1.dto.trade.BitfinexOrderStatusResponse;
import org.knowm.xchange.currency.Currency;
import org.knowm.xchange.dto.Order;
import org.knowm.xchange.dto.account.Balance;
import org.knowm.xchange.dto.trade.OpenOrders;
import org.knowm.xchange.dto.trade.UserTrade;
import org.knowm.xchange.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class BitfinexStreamingAdapters {

  private static final Logger LOG = LoggerFactory.getLogger(BitfinexStreamingAdapters.class);

  private static final BigDecimal THOUSAND = new BigDecimal(1000);

  @Nullable
  static BitfinexWebSocketAuthPreTrade adaptPreTrade(JsonNode preTrade) {
    if (preTrade.size() < 12) {
      LOG.error(
          "addPreTrade unexpected record size={}, record={}", preTrade.size(), preTrade.toString());
      return null;
    }
    long id = preTrade.get(0).longValue();
    String pair = preTrade.get(1).textValue();
    long mtsCreate = preTrade.get(2).longValue();
    long orderId = preTrade.get(3).longValue();
    BigDecimal execAmount = preTrade.get(4).decimalValue();
    BigDecimal execPrice = preTrade.get(5).decimalValue();
    String orderType = preTrade.get(6).textValue();
    BigDecimal orderPrice = preTrade.get(7).decimalValue();
    int maker = preTrade.get(8).intValue();
    BitfinexWebSocketAuthPreTrade preTradeObject =
        new BitfinexWebSocketAuthPreTrade(
            id, pair, mtsCreate, orderId, execAmount, execPrice, orderType, orderPrice, maker);
    LOG.debug("New pre trade: {}", preTradeObject);
    return preTradeObject;
  }

  @Nullable
  static BitfinexWebSocketAuthTrade adaptTrade(JsonNode trade) {
    if (trade.size() < 11) {
      LOG.error("addTrade unexpected record size={}, record={}", trade.size(), trade.toString());
      return null;
    }
    long id = trade.get(0).longValue();
    String pair = trade.get(1).textValue();
    long mtsCreate = trade.get(2).longValue();
    long orderId = trade.get(3).longValue();
    BigDecimal execAmount = trade.get(4).decimalValue();
    BigDecimal execPrice = trade.get(5).decimalValue();
    String orderType = trade.get(6).textValue();
    BigDecimal orderPrice = trade.get(7).decimalValue();
    int maker = trade.get(8).intValue();
    BigDecimal fee = trade.get(9).decimalValue();
    String currency = trade.get(10).textValue();
    BitfinexWebSocketAuthTrade tradeObject =
        new BitfinexWebSocketAuthTrade(
            id,
            pair,
            mtsCreate,
            orderId,
            execAmount,
            execPrice,
            orderType,
            orderPrice,
            maker,
            fee,
            currency);
    LOG.debug("New trade: {}", tradeObject);
    return tradeObject;
  }

  static Stream adaptOrders(JsonNode orders) {
    Iterable iterator = () -> orders.iterator();
    return stream(iterator.spliterator(), false)
        .filter(o -> o.size() >= 32)
        .map(BitfinexStreamingAdapters::createOrderObject)
        .peek(o -> LOG.debug("New order: {}", o));
  }

  @Nullable
  static BitfinexWebSocketAuthOrder adaptOrder(JsonNode order) {
    BitfinexWebSocketAuthOrder orderObject = createOrderObject(order);
    if (orderObject == null) {
      return null;
    }
    LOG.debug("Updated order: {}", orderObject);
    return orderObject;
  }

  @Nullable
  static BitfinexWebSocketAuthBalance adaptBalance(JsonNode balance) {
    BitfinexWebSocketAuthBalance balanceObject = createBalanceObject(balance);
    if (balanceObject == null) {
      return null;
    }
    LOG.debug("Balance: {}", balanceObject);
    return balanceObject;
  }

  static Stream adaptBalances(JsonNode balances) {
    Iterable iterator = () -> balances.iterator();
    return stream(iterator.spliterator(), false)
        .filter(o -> o.size() >= 5)
        .map(BitfinexStreamingAdapters::createBalanceObject)
        .peek(o -> LOG.debug("Balance: {}", o));
  }

  @Nullable
  private static BitfinexWebSocketAuthBalance createBalanceObject(JsonNode balance) {
    if (balance.size() < 5) {
      LOG.error(
          "createBalanceObject unexpected record size={}, record={}",
          balance.size(),
          balance.toString());
      return null;
    }

    String walletType = balance.get(0).textValue();
    String currency = balance.get(1).textValue();
    BigDecimal balanceValue = balance.get(2).decimalValue();
    BigDecimal unsettledInterest = balance.get(3).decimalValue();
    BigDecimal balanceAvailable =
        balance.get(4).asText().equals("null") ? null : balance.get(4).decimalValue();

    return new BitfinexWebSocketAuthBalance(
        walletType, currency, balanceValue, unsettledInterest, balanceAvailable);
  }

  @Nullable
  private static BitfinexWebSocketAuthOrder createOrderObject(JsonNode order) {
    if (order.size() < 32) {
      LOG.error(
          "createOrderObject unexpected record size={}, record={}", order.size(), order.toString());
      return null;
    }

    long id = order.get(0).longValue();
    long groupId = order.get(1).longValue();
    long cid = order.get(2).longValue();
    String symbol = order.get(3).textValue();
    long mtsCreate = order.get(4).longValue();
    long mtsUpdate = order.get(5).longValue();
    BigDecimal amount = order.get(6).decimalValue();
    BigDecimal amountOrig = order.get(7).decimalValue();
    String type = order.get(8).textValue();
    String typePrev = order.get(9).textValue();
    int flags = order.get(12).intValue();
    String orderStatus = order.get(13).textValue();
    BigDecimal price = order.get(16).decimalValue();
    BigDecimal priceAvg = order.get(17).decimalValue();
    BigDecimal priceTrailing = order.get(18).decimalValue();
    BigDecimal priceAuxLimit = order.get(19).decimalValue();
    long placedId = order.get(25).longValue();

    return new BitfinexWebSocketAuthOrder(
        id,
        groupId,
        cid,
        symbol,
        mtsCreate,
        mtsUpdate,
        amount,
        amountOrig,
        type,
        typePrev,
        orderStatus,
        price,
        priceAvg,
        priceTrailing,
        priceAuxLimit,
        placedId,
        flags);
  }

  private static BitfinexOrderType adaptV2OrderTypeToV1(String orderType) {
    switch (orderType) {
      case "LIMIT":
        return BitfinexOrderType.MARGIN_LIMIT;
      case "MARKET":
        return BitfinexOrderType.MARGIN_MARKET;
      case "STOP":
        return BitfinexOrderType.MARGIN_STOP;
      case "TRAILING STOP":
        return BitfinexOrderType.MARGIN_TRAILING_STOP;
      case "EXCHANGE MARKET":
        return BitfinexOrderType.MARKET;
      case "EXCHANGE LIMIT":
        return BitfinexOrderType.LIMIT;
      case "EXCHANGE STOP":
        return BitfinexOrderType.STOP;
      case "EXCHANGE TRAILING STOP":
        return BitfinexOrderType.TRAILING_STOP;
      case "FOK":
        return BitfinexOrderType.MARGIN_FILL_OR_KILL;
      case "EXCHANGE FOK":
        return BitfinexOrderType.FILL_OR_KILL;
      default:
        return BitfinexOrderType.LIMIT; // Safe fallback
    }
  }

  private static String adaptV2SymbolToV1(String symbol) {
    return symbol.substring(1);
  }

  /**
   * We adapt the websocket message to what we expect from the V1 REST API, so that we don't
   * re-implement the complex logic which works out whether we need limit orders, stop orders,
   * market orders etc.
   */
  private static BitfinexOrderStatusResponse adaptOrderToRestResponse(
      BitfinexWebSocketAuthOrder authOrder) {
    int signum = authOrder.getAmountOrig().signum();
    return new BitfinexOrderStatusResponse(
        authOrder.getId(),
        adaptV2SymbolToV1(authOrder.getSymbol()),
        authOrder.getPrice(),
        authOrder.getPriceAvg(),
        signum > 0 ? "buy" : "sell",
        adaptV2OrderTypeToV1(authOrder.getType()).getValue(),
        new BigDecimal(authOrder.getMtsCreate()).divide(THOUSAND),
        "ACTIVE".equals(authOrder.getOrderStatus()),
        "CANCELED".equals(authOrder.getOrderStatus())
            || "FILLORKILL CANCELED".equals(authOrder.getOrderStatus()),
        false, // wasForced,
        signum >= 0 ? authOrder.getAmountOrig() : authOrder.getAmountOrig().negate(),
        signum >= 0 ? authOrder.getAmount() : authOrder.getAmount().negate(),
        signum >= 0
            ? authOrder.getAmountOrig().subtract(authOrder.getAmount())
            : authOrder.getAmountOrig().subtract(authOrder.getAmount()).negate());
  }

  static Order adaptOrder(BitfinexWebSocketAuthOrder authOrder) {
    BitfinexOrderStatusResponse[] orderStatus = {adaptOrderToRestResponse(authOrder)};
    OpenOrders orders = BitfinexAdapters.adaptOrders(orderStatus);
    if (orders.getOpenOrders().isEmpty()) {
      if (orders.getHiddenOrders().isEmpty()) {
        throw new IllegalStateException("No order in message");
      }
      return orders.getHiddenOrders().get(0);
    }
    return orders.getOpenOrders().get(0);
  }

  static UserTrade adaptUserTrade(BitfinexWebSocketAuthTrade authTrade) {
    return new UserTrade.Builder()
        .currencyPair(BitfinexAdapters.adaptCurrencyPair(adaptV2SymbolToV1(authTrade.getPair())))
        .feeAmount(authTrade.getFee().abs())
        .feeCurrency(Currency.getInstance(authTrade.getFeeCurrency()))
        .id(Long.toString(authTrade.getId()))
        .orderId(Long.toString(authTrade.getOrderId()))
        .originalAmount(authTrade.getExecAmount().abs())
        .price(authTrade.getExecPrice())
        .timestamp(DateUtils.fromMillisUtc(authTrade.getMtsCreate()))
        .type(authTrade.getExecAmount().signum() == 1 ? BID : ASK)
        .build();
  }

  static Balance adaptBalance(BitfinexWebSocketAuthBalance authBalance) {
    return new Balance(
        Currency.getInstance(authBalance.getCurrency()),
        authBalance.getBalance(),
        authBalance.getBalanceAvailable());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy