org.knowm.xchange.bitmex.BitmexAdapters Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xchange-bitmex Show documentation
Show all versions of xchange-bitmex Show documentation
XChange implementations for the BitMex Exchange.
The newest version!
package org.knowm.xchange.bitmex;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.knowm.xchange.bitmex.dto.account.BitmexTicker;
import org.knowm.xchange.bitmex.dto.account.BitmexWalletTransaction;
import org.knowm.xchange.bitmex.dto.marketdata.BitmexDepth;
import org.knowm.xchange.bitmex.dto.marketdata.BitmexPrivateOrder;
import org.knowm.xchange.bitmex.dto.marketdata.BitmexPublicOrder;
import org.knowm.xchange.bitmex.dto.marketdata.BitmexPublicOrderList;
import org.knowm.xchange.bitmex.dto.marketdata.BitmexPublicTrade;
import org.knowm.xchange.bitmex.dto.trade.BitmexOrder;
import org.knowm.xchange.bitmex.dto.trade.BitmexOrderDescription;
import org.knowm.xchange.bitmex.dto.trade.BitmexOrderResponse;
import org.knowm.xchange.bitmex.dto.trade.BitmexOrderStatus;
import org.knowm.xchange.bitmex.dto.trade.BitmexPrivateExecution;
import org.knowm.xchange.bitmex.dto.trade.BitmexSide;
import org.knowm.xchange.currency.Currency;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.Order.OrderStatus;
import org.knowm.xchange.dto.Order.OrderType;
import org.knowm.xchange.dto.account.Balance;
import org.knowm.xchange.dto.account.FundingRecord;
import org.knowm.xchange.dto.account.Wallet;
import org.knowm.xchange.dto.marketdata.OrderBook;
import org.knowm.xchange.dto.marketdata.Ticker;
import org.knowm.xchange.dto.marketdata.Trade;
import org.knowm.xchange.dto.marketdata.Trades;
import org.knowm.xchange.dto.marketdata.Trades.TradeSortType;
import org.knowm.xchange.dto.meta.InstrumentMetaData;
import org.knowm.xchange.dto.trade.LimitOrder;
import org.knowm.xchange.dto.trade.OpenOrders;
import org.knowm.xchange.dto.trade.UserTrade;
import org.knowm.xchange.exceptions.ExchangeException;
public class BitmexAdapters {
private static final BigDecimal SATOSHIS_BY_BTC = BigDecimal.valueOf(100_000_000L);
public static OrderBook adaptOrderBook(BitmexDepth bitmexDepth, CurrencyPair currencyPair) {
OrdersContainer asksOrdersContainer =
adaptOrders(bitmexDepth.getAsks(), currencyPair, OrderType.ASK, true);
OrdersContainer bidsOrdersContainer =
adaptOrders(bitmexDepth.getBids(), currencyPair, OrderType.BID, false);
return new OrderBook(
new Date(Math.max(asksOrdersContainer.getTimestamp(), bidsOrdersContainer.getTimestamp())),
asksOrdersContainer.getLimitOrders(),
bidsOrdersContainer.getLimitOrders());
}
public static BitmexDepth adaptDepth(BitmexPublicOrderList orders) {
BitmexDepth bitmexDepth = new BitmexDepth(new ArrayList<>(), new ArrayList<>());
for (BitmexPublicOrder bitmexOrder : orders) {
if (bitmexOrder.getSide().equals(BitmexSide.BUY)) {
bitmexDepth.getBids().add(bitmexOrder);
} else if (bitmexOrder.getSide().equals(BitmexSide.SELL)) {
bitmexDepth.getAsks().add(bitmexOrder);
}
}
return bitmexDepth;
}
public static OrdersContainer adaptOrders(
List orders,
CurrencyPair currencyPair,
OrderType orderType,
boolean reverse) {
// bitmex does not provide timestamps on order book
long maxTimestamp = System.currentTimeMillis();
LimitOrder[] limitOrders = new LimitOrder[orders.size()];
int i = reverse ? orders.size() - 1 : 0;
for (BitmexPublicOrder order : orders) {
limitOrders[i] = adaptOrder(order, orderType, currencyPair);
i += (reverse ? -1 : 1);
}
return new OrdersContainer(maxTimestamp, Arrays.asList(limitOrders));
}
public static Trades adaptTrades(List trades, CurrencyPair currencyPair) {
List tradeList = new ArrayList<>(trades.size());
for (int i = 0; i < trades.size(); i++) {
BitmexPublicTrade trade = trades.get(i);
tradeList.add(adaptTrade(trade, currencyPair));
}
long lastTid = trades.size() > 0 ? (trades.get(0).getTime().getTime()) : 0;
// long lastTid = 0L;
return new Trades(tradeList, lastTid, TradeSortType.SortByTimestamp);
}
public static LimitOrder adaptOrder(
BitmexPublicOrder order, OrderType orderType, CurrencyPair currencyPair) {
BigDecimal volume = order.getVolume();
return new LimitOrder(orderType, volume, currencyPair, "", null, order.getPrice());
}
public static LimitOrder adaptOrder(BitmexPrivateOrder rawOrder) {
OrderType type = rawOrder.getSide() == BitmexSide.BUY ? OrderType.BID : OrderType.ASK;
CurrencyPair pair = adaptSymbolToCurrencyPair(rawOrder.getSymbol());
return new LimitOrder(
type,
rawOrder.getVolume(),
pair,
rawOrder.getId(),
rawOrder.getTimestamp(),
rawOrder.getPrice(),
rawOrder.getAvgPx(),
rawOrder.getCumQty(),
null,
BitmexAdapters.adaptOrderStatus(rawOrder.getOrderStatus()));
}
public static Ticker adaptTicker(BitmexTicker bitmexTicker, CurrencyPair currencyPair) {
Ticker.Builder builder = new Ticker.Builder();
builder.open(bitmexTicker.getPrevClosePrice());
builder.ask(bitmexTicker.getAskPrice());
builder.bid(bitmexTicker.getBidPrice());
builder.last(bitmexTicker.getLastPrice());
builder.high(bitmexTicker.getHighPrice());
builder.low(bitmexTicker.getLowPrice());
builder.vwap(new BigDecimal(bitmexTicker.getVwap().longValue()));
builder.volume(bitmexTicker.getVolume24h());
builder.currencyPair(currencyPair);
return builder.build();
}
public static Trade adaptTrade(BitmexPublicTrade bitmexPublicTrade, CurrencyPair currencyPair) {
OrderType type = adaptOrderType(bitmexPublicTrade.getSide());
BigDecimal originalAmount = bitmexPublicTrade.getSize();
Date timestamp = bitmexPublicTrade.getTime();
// Date timestamp = adaptTimestamp(bitmexPublicTrade.getTime());
// new Date((long) (bitmexPublicTrade.getTime()));
return new Trade.Builder()
.type(type)
.originalAmount(originalAmount)
.currencyPair(currencyPair)
.price(bitmexPublicTrade.getPrice())
.timestamp(timestamp)
.id(String.valueOf(timestamp.getTime()))
.build();
}
public static Wallet adaptWallet(Map bitmexWallet) {
List balances = new ArrayList<>(bitmexWallet.size());
for (Entry balancePair : bitmexWallet.entrySet()) {
Currency currency = new Currency(balancePair.getKey());
Balance balance = new Balance(currency, balancePair.getValue());
balances.add(balance);
}
return Wallet.Builder.from(balances).build();
}
public static OpenOrders adaptOpenOrders(Map bitmexOrders) {
List limitOrders = new ArrayList<>();
for (Entry bitmexOrderEntry : bitmexOrders.entrySet()) {
BitmexOrder bitmexOrder = bitmexOrderEntry.getValue();
BitmexOrderDescription orderDescription = bitmexOrder.getOrderDescription();
if (!"limit".equals(orderDescription.getOrderType().toString())) {
// how to handle stop-loss, take-profit, stop-loss-limit, and so on orders?
// ignore anything but a plain limit order for now
continue;
}
limitOrders.add(adaptLimitOrder(bitmexOrder, bitmexOrderEntry.getKey()));
}
return new OpenOrders(limitOrders);
}
public static LimitOrder adaptLimitOrder(BitmexOrder bitmexOrder, String id) {
BitmexOrderDescription orderDescription = bitmexOrder.getOrderDescription();
OrderType type = adaptOrderType(orderDescription.getType());
BigDecimal originalAmount = bitmexOrder.getVolume();
BigDecimal filledAmount = bitmexOrder.getVolumeExecuted();
CurrencyPair pair = BitmexAdapters.adaptSymbolToCurrencyPair(orderDescription.getAssetPair());
Date timestamp = new Date((long) (bitmexOrder.getOpenTimestamp() * 1000L));
OrderStatus status = adaptOrderStatus(bitmexOrder.getStatus());
if (status == OrderStatus.NEW
&& filledAmount.compareTo(BigDecimal.ZERO) > 0
&& filledAmount.compareTo(originalAmount) < 0) {
status = OrderStatus.PARTIALLY_FILLED;
}
return new LimitOrder(
type,
originalAmount,
pair,
id,
timestamp,
orderDescription.getPrice(),
orderDescription.getPrice(),
filledAmount,
bitmexOrder.getFee(),
status);
}
public static OrderType adaptOrderType(BitmexSide bitmexType) {
return bitmexType == null
? null
: bitmexType.equals(BitmexSide.BUY) ? OrderType.BID : OrderType.ASK;
}
public static String adaptOrderId(BitmexOrderResponse orderResponse) {
List orderIds = orderResponse.getTransactionIds();
return (orderIds == null || orderIds.isEmpty()) ? "" : orderIds.get(0);
}
private static InstrumentMetaData adaptPair(
BitmexTicker ticker, InstrumentMetaData originalMeta) {
if (originalMeta != null) {
return new InstrumentMetaData.Builder()
.tradingFee(ticker.getTakerFee())
.minimumAmount(originalMeta.getMinimumAmount())
.maximumAmount(originalMeta.getMaximumAmount())
.priceScale(Math.max(0, ticker.getTickSize().stripTrailingZeros().scale()))
.feeTiers(originalMeta.getFeeTiers())
.build();
} else {
return new InstrumentMetaData.Builder()
.tradingFee(ticker.getTakerFee())
.priceScale(Math.max(0, ticker.getTickSize().stripTrailingZeros().scale()))
.build();
}
}
public static OrderStatus adaptOrderStatus(BitmexOrderStatus status) {
switch (status) {
case PENDING:
return OrderStatus.PENDING_NEW;
case OPEN:
return OrderStatus.NEW;
case CLOSED:
return OrderStatus.FILLED;
case CANCELED:
return OrderStatus.CANCELED;
case EXPIRED:
return OrderStatus.EXPIRED;
case REJECTED:
return OrderStatus.REJECTED;
default:
return null;
}
}
public static OrderStatus adaptOrderStatus(BitmexPrivateOrder.OrderStatus status) {
switch (status) {
case New:
return OrderStatus.NEW;
case PartiallyFilled:
return OrderStatus.PARTIALLY_FILLED;
case Filled:
return OrderStatus.FILLED;
case Canceled:
return OrderStatus.CANCELED;
case Rejected:
return OrderStatus.REJECTED;
default:
return null;
}
}
public static String adaptCurrency(Currency currency) {
// bitmex seems to use a lowercase 't' in XBT
// can test this here - https://testnet.bitmex.com/api/explorer/#!/User/User_getDepositAddress
// uppercase 'T' will return 'Unknown currency code'
if (currency.getCurrencyCode().equals("BTC") || currency.getCurrencyCode().equals("XBT")) {
return "XBt";
}
return currency.getCurrencyCode();
}
public static Currency adaptCurrency(String currencyCode) {
if (currencyCode.equalsIgnoreCase("XBt")) {
return Currency.BTC;
}
return Currency.getInstance(currencyCode);
}
public static String adaptCurrencyPairToSymbol(CurrencyPair currencyPair) {
return currencyPair == null
? null
: currencyPair.base.getCurrencyCode() + currencyPair.counter.getCurrencyCode();
}
public static CurrencyPair adaptSymbolToCurrencyPair(String bitmexSymbol) {
// Assuming that base symbol has 3 characters
String baseSymbol = bitmexSymbol.substring(0, 3);
String counterSymbol = bitmexSymbol.substring(3);
return new CurrencyPair(baseSymbol, counterSymbol);
}
public static class OrdersContainer {
private final long timestamp;
private final List limitOrders;
/**
* Constructor
*
* @param timestamp
* @param limitOrders
*/
public OrdersContainer(long timestamp, List limitOrders) {
this.timestamp = timestamp;
this.limitOrders = limitOrders;
}
public long getTimestamp() {
return timestamp;
}
public List getLimitOrders() {
return limitOrders;
}
}
public static UserTrade adoptUserTrade(BitmexPrivateExecution exec) {
CurrencyPair pair = BitmexAdapters.adaptSymbolToCurrencyPair(exec.symbol);
// the "lastQty" parameter is in the USD currency for ???/USD pairs
OrderType orderType = convertType(exec.side);
return orderType == null
? null
: UserTrade.builder()
.id(exec.execID)
.orderId(exec.orderID)
.currencyPair(pair)
.originalAmount(exec.lastQty)
.price(exec.lastPx)
.feeAmount(exec.execComm.divide(SATOSHIS_BY_BTC, MathContext.DECIMAL32))
.feeCurrency(Currency.XBT)
.timestamp(exec.timestamp)
.type(orderType)
.orderUserReference(exec.clOrdID)
.build();
}
private static OrderType convertType(String side) {
switch (side) {
case "Buy":
return OrderType.BID;
case "Sell":
return OrderType.ASK;
default:
return null;
}
}
public static FundingRecord adaptFundingRecord(BitmexWalletTransaction walletTransaction) {
return new FundingRecord(
walletTransaction.getAddress(),
walletTransaction.getTransactTime(),
adaptCurrency(walletTransaction.getCurrency()),
walletTransaction.getAmount().abs(),
walletTransaction.getTransactID(),
walletTransaction.getTx(),
adaptFundingRecordtype(walletTransaction),
adaptFundingRecordStatus(walletTransaction.getTransactStatus()),
walletTransaction.getWalletBalance(),
walletTransaction.getFee(),
walletTransaction.getText());
}
private static FundingRecord.Type adaptFundingRecordtype(
final BitmexWalletTransaction walletTransaction) {
String type = walletTransaction.getTransactType();
if (type.equalsIgnoreCase("Deposit")) {
return FundingRecord.Type.DEPOSIT;
} else if (type.equalsIgnoreCase("Withdrawal")) {
return FundingRecord.Type.WITHDRAWAL;
} else if (type.equalsIgnoreCase("RealisedPNL") || type.equalsIgnoreCase("UnrealisedPNL")) {
// 'RealisedPNL' will always have transactStatus = Completed whereas 'UnrealisedPNL' will
// always be transactStatus = Pending
if (walletTransaction.getAmount().compareTo(BigDecimal.ZERO) > 0) {
return FundingRecord.Type.REALISED_PROFIT;
} else if (walletTransaction.getAmount().compareTo(BigDecimal.ZERO) < 0) {
return FundingRecord.Type.REALISED_LOSS;
}
}
throw new ExchangeException("Unknown FundingRecord.Type");
}
private static FundingRecord.Status adaptFundingRecordStatus(final String transactStatus) {
return FundingRecord.Status.resolveStatus(transactStatus);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy