package org.knowm.xchange.btcchina;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.knowm.xchange.btcchina.dto.BTCChinaResponse;
import org.knowm.xchange.btcchina.dto.BTCChinaValue;
import org.knowm.xchange.btcchina.dto.account.BTCChinaAccountInfo;
import org.knowm.xchange.btcchina.dto.marketdata.BTCChinaDepth;
import org.knowm.xchange.btcchina.dto.marketdata.BTCChinaTicker;
import org.knowm.xchange.btcchina.dto.marketdata.BTCChinaTickerObject;
import org.knowm.xchange.btcchina.dto.marketdata.BTCChinaTrade;
import org.knowm.xchange.btcchina.dto.trade.BTCChinaMarketDepth;
import org.knowm.xchange.btcchina.dto.trade.BTCChinaMarketDepthOrder;
import org.knowm.xchange.btcchina.dto.trade.BTCChinaOrder;
import org.knowm.xchange.btcchina.dto.trade.BTCChinaOrders;
import org.knowm.xchange.btcchina.dto.trade.BTCChinaTransaction;
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.AccountInfo;
import org.knowm.xchange.dto.account.Balance;
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.CurrencyMetaData;
import org.knowm.xchange.dto.meta.CurrencyPairMetaData;
import org.knowm.xchange.dto.meta.ExchangeMetaData;
import org.knowm.xchange.dto.trade.LimitOrder;
import org.knowm.xchange.dto.trade.UserTrade;
import org.knowm.xchange.dto.trade.UserTrades;
import org.knowm.xchange.utils.DateUtils;
/**
* Various adapters for converting from BTCChina DTOs to XChange DTOs.
*/
public final class BTCChinaAdapters {
private static final int TICKER_MARKET_KEY_PREFIX_LENGTH = "ticker_".length();
private static final int ORDERS_MARKET_KEY_PREFIX_LENGTH = "order_".length();
/**
* private Constructor
*/
private BTCChinaAdapters() {
}
/**
* Adapts an array of btcchinaOrders to a List of LimitOrders
*/
public static List<LimitOrder> adaptOrders(BigDecimal[][] btcchinaOrders, CurrencyPair currencyPair, OrderType orderType) {
List<LimitOrder> limitOrders = new ArrayList<LimitOrder>(btcchinaOrders.length);
for (BigDecimal[] btcchinaOrder : btcchinaOrders) {
limitOrders.add(adaptOrder(btcchinaOrder[1], btcchinaOrder[0], currencyPair, orderType));
}
return limitOrders;
}
/**
* Adapts a BTCChinaOrder to a LimitOrder
*/
public static LimitOrder adaptOrder(BigDecimal amount, BigDecimal price, CurrencyPair currencyPair, OrderType orderType) {
return new LimitOrder(orderType, amount, currencyPair, "", null, price);
}
/**
* Adapts a BTCChinaTrade to a Trade Object
*
* @param btcChinaTrade A BTCChina trade
* @return The XChange Trade
*/
public static Trade adaptTrade(BTCChinaTrade btcChinaTrade, CurrencyPair currencyPair) {
BigDecimal amount = btcChinaTrade.getAmount();
BigDecimal price = btcChinaTrade.getPrice();
Date date = adaptDate(btcChinaTrade.getDate());
OrderType orderType = btcChinaTrade.getOrderType().equals("sell") ? OrderType.ASK : OrderType.BID;
final String tradeId = String.valueOf(btcChinaTrade.getTid());
return new Trade(orderType, amount, currencyPair, price, date, tradeId);
}
public static Trades adaptTrades(List<BTCChinaTrade> btcchinaTrades, CurrencyPair currencyPair) {
return adaptTrades(btcchinaTrades.toArray(new BTCChinaTrade[0]), currencyPair);
}
/**
* Adapts a BTCChinaTrade[] to a Trades Object.
*
* @param btcchinaTrades The BTCChina trade data
* @return The trades
*/
public static Trades adaptTrades(BTCChinaTrade[] btcchinaTrades, CurrencyPair currencyPair) {
List<Trade> tradesList = new ArrayList<Trade>(btcchinaTrades.length);
long latestTradeId = 0;
for (BTCChinaTrade btcchinaTrade : btcchinaTrades) {
long tradeId = btcchinaTrade.getTid();
if (tradeId > latestTradeId) {
latestTradeId = tradeId;
}
tradesList.add(adaptTrade(btcchinaTrade, currencyPair));
}
return new Trades(tradesList, latestTradeId, TradeSortType.SortByID);
}
/**
* Adapts a BTCChinaTicker to a Ticker Object
*/
public static Ticker adaptTicker(BTCChinaTicker btcChinaTicker, CurrencyPair currencyPair) {
return adaptTicker(btcChinaTicker.getTicker(), currencyPair);
}
public static Ticker adaptTicker(BTCChinaTickerObject ticker, CurrencyPair currencyPair) {
BigDecimal last = ticker.getLast();
BigDecimal high = ticker.getHigh();
BigDecimal low = ticker.getLow();
BigDecimal vwap = ticker.getVwap();
BigDecimal buy = ticker.getBuy();
BigDecimal sell = ticker.getSell();
BigDecimal volume = ticker.getVol();
Date date = adaptDate(ticker.getDate());
return new Ticker.Builder().currencyPair(currencyPair).last(last).high(high).low(low).vwap(vwap).bid(buy).ask(sell).volume(volume).timestamp(date)
.build();
}
public static Map<CurrencyPair, Ticker> adaptTickers(BTCChinaTicker btcChinaTicker) {
Map<CurrencyPair, Ticker> tickers = new LinkedHashMap<CurrencyPair, Ticker>(btcChinaTicker.size());
for (Map.Entry<String, BTCChinaTickerObject> entry : btcChinaTicker.entrySet()) {
CurrencyPair currencyPair = adaptCurrencyPairFromTickerMarketKey(entry.getKey());
tickers.put(currencyPair, adaptTicker(entry.getValue(), currencyPair));
}
return tickers;
}
/**
* Adapts a BTCChinaAccountInfoResponse to Wallet Object
*/
public static AccountInfo adaptAccountInfo(BTCChinaResponse<BTCChinaAccountInfo> response) {
BTCChinaAccountInfo result = response.getResult();
return new AccountInfo(result.getProfile().getUsername(), result.getProfile().getTradeFee(),
BTCChinaAdapters.adaptWallet(result.getBalances(), result.getFrozens(), result.getLoans()));
}
public static Wallet adaptWallet(Map<String, BTCChinaValue> balances, Map<String, BTCChinaValue> frozens, Map<String, BTCChinaValue> loans) {
List<Balance> wallet = new ArrayList<Balance>(balances.size());
for (Map.Entry<String, BTCChinaValue> entry : balances.entrySet()) {
BTCChinaValue frozen = frozens.get(entry.getKey());
BTCChinaValue loan = loans.get(entry.getKey());
BigDecimal balanceAmount = BTCChinaUtils.valueToBigDecimal(entry.getValue());
BigDecimal frozenAmount = frozen == null ? BigDecimal.ZERO : BTCChinaUtils.valueToBigDecimal(frozen);
BigDecimal loanAmount = loan == null ? BigDecimal.ZERO : BTCChinaUtils.valueToBigDecimal(loan);
wallet.add(new Balance.Builder().currency(Currency.getInstance(entry.getValue().getCurrency())).available(balanceAmount).frozen(frozenAmount)
.borrowed(loanAmount).build());
}
return new Wallet(wallet);
}
/**
* Adapts {@link BTCChinaDepth} to {@link OrderBook}.
*
* @param btcChinaDepth {@link BTCChinaDepth}
* @param currencyPair the currency pair of the depth.
* @return {@link OrderBook}
*/
public static OrderBook adaptOrderBook(BTCChinaDepth btcChinaDepth, CurrencyPair currencyPair) {
List<LimitOrder> asks = BTCChinaAdapters.adaptOrders(btcChinaDepth.getAsksArray(), currencyPair, OrderType.ASK);
Collections.reverse(asks);
List<LimitOrder> bids = BTCChinaAdapters.adaptOrders(btcChinaDepth.getBidsArray(), currencyPair, OrderType.BID);
return new OrderBook(BTCChinaAdapters.adaptDate(btcChinaDepth.getDate()), asks, bids);
}
public static OrderBook adaptOrderBook(BTCChinaMarketDepth marketDepth, CurrencyPair currencyPair) {
List<LimitOrder> asks = adaptLimitOrders(marketDepth.getAsks(), OrderType.ASK, currencyPair);
List<LimitOrder> bids = adaptLimitOrders(marketDepth.getBids(), OrderType.BID, currencyPair);
return new OrderBook(adaptDate(marketDepth.getDate()), asks, bids);
}
public static List<LimitOrder> adaptLimitOrders(BTCChinaMarketDepthOrder[] orders, OrderType orderType, CurrencyPair currencyPair) {
List<LimitOrder> limitOrders = new ArrayList<LimitOrder>(orders.length);
for (BTCChinaMarketDepthOrder order : orders) {
limitOrders.add(adaptLimitOrder(order, orderType, currencyPair));
}
return limitOrders;
}
public static LimitOrder adaptLimitOrder(BTCChinaMarketDepthOrder order, OrderType orderType, CurrencyPair currencyPair) {
return new LimitOrder.Builder(orderType, currencyPair).limitPrice(order.getPrice()).tradableAmount(order.getAmount()).build();
}
public static List<LimitOrder> adaptOrders(BTCChinaOrder[] orders, CurrencyPair currencyPair) {
List<LimitOrder> limitOrders = new ArrayList<LimitOrder>(orders.length);
for (BTCChinaOrder order : orders) {
LimitOrder limitOrder = adaptLimitOrder(order, currencyPair);
limitOrders.add(limitOrder);
}
return limitOrders;
}
public static List<LimitOrder> adaptOrders(BTCChinaOrders orders, CurrencyPair specifiedCurrencyPair) {
List<LimitOrder> limitOrders = new ArrayList<LimitOrder>();
BTCChinaOrder[] certainCurrencyPairOrders = orders.getOrdersArray();
if (certainCurrencyPairOrders != null) {
limitOrders.addAll(adaptOrders(certainCurrencyPairOrders, specifiedCurrencyPair));
}
for (Map.Entry<String, BTCChinaOrder[]> entry : orders.entrySet()) {
CurrencyPair currencyPair = adaptCurrencyPairFromOrdersMarketKey(entry.getKey());
limitOrders.addAll(adaptOrders(entry.getValue(), currencyPair));
}
return limitOrders;
}
/**
* Adapts BTCChinaOrder to LimitOrder.
*/
public static LimitOrder adaptLimitOrder(BTCChinaOrder order, CurrencyPair currencyPair) {
OrderType orderType = order.getType().equals("bid") ? OrderType.BID : OrderType.ASK;
BigDecimal amount = order.getAmount();
String id = Long.toString(order.getId());
Date date = adaptDate(order.getDate());
BigDecimal price = order.getPrice();
return new LimitOrder(orderType, amount, currencyPair, id, date, price);
}
public static UserTrade adaptTransaction(BTCChinaTransaction transaction) {
final String type = transaction.getType();
// could also be 'rebate' or other
if (!(type.startsWith("buy") || type.startsWith("sell"))) {
return null;
}
final OrderType orderType = type.startsWith("buy") ? OrderType.BID : OrderType.ASK;
final CurrencyPair currencyPair = adaptCurrencyPair(transaction.getMarket());
final BigDecimal amount;
final BigDecimal money;
final int scale;
if (currencyPair.equals(CurrencyPair.BTC_CNY)) {
amount = transaction.getBtcAmount().abs();
money = transaction.getCnyAmount().abs();
scale = BTCChinaExchange.CNY_SCALE;
} else if (currencyPair.equals(CurrencyPair.LTC_CNY)) {
amount = transaction.getLtcAmount().abs();
money = transaction.getCnyAmount().abs();
scale = BTCChinaExchange.CNY_SCALE;
} else if (currencyPair.equals(CurrencyPair.LTC_BTC)) {
amount = transaction.getLtcAmount().abs();
money = transaction.getBtcAmount().abs();
scale = BTCChinaExchange.BTC_SCALE;
} else {
throw new IllegalArgumentException("Unknown currency pair: " + currencyPair);
}
final BigDecimal price = money.divide(amount, scale, RoundingMode.HALF_EVEN);
final Date date = adaptDate(transaction.getDate());
final String tradeId = String.valueOf(transaction.getId());
return new UserTrade(orderType, amount, currencyPair, price, date, tradeId, null, null, (Currency) null);
}
/**
* Adapt BTCChinaTransactions to Trades.
*/
public static UserTrades adaptTransactions(List<BTCChinaTransaction> transactions) {
List<UserTrade> tradeHistory = new ArrayList<UserTrade>(transactions.size());
for (BTCChinaTransaction transaction : transactions) {
UserTrade adaptTransaction = adaptTransaction(transaction);
if (adaptTransaction != null) {
tradeHistory.add(adaptTransaction);
}
}
return new UserTrades(tradeHistory, TradeSortType.SortByID);
}
public static String adaptMarket(CurrencyPair currencyPair) {
return currencyPair.base.getCurrencyCode().toLowerCase() + currencyPair.counter.getCurrencyCode().toLowerCase();
}
public static CurrencyPair adaptCurrencyPairFromTickerMarketKey(String market) {
return adaptCurrencyPair(market.substring(TICKER_MARKET_KEY_PREFIX_LENGTH));
}
public static CurrencyPair adaptCurrencyPairFromOrdersMarketKey(String market) {
return adaptCurrencyPair(market.substring(ORDERS_MARKET_KEY_PREFIX_LENGTH));
}
public static CurrencyPair adaptCurrencyPair(String market) {
return new CurrencyPair(market.substring(0, 3).toUpperCase(), market.substring(3).toUpperCase());
}
public static Date adaptDate(long date) {
return DateUtils.fromMillisUtc(date * 1000L);
}
public static OrderType adaptOrderType(String type) {
return type.equals("buy") ? OrderType.BID : OrderType.ASK;
}
public static OrderStatus adaptOrderStatus(String status) {
switch (status.toUpperCase()) {
case "OPEN":
return OrderStatus.NEW;
case "CLOSED":
return OrderStatus.FILLED;
case "CANCELLED":
return OrderStatus.CANCELED;
case "PENDING":
return OrderStatus.PENDING_NEW;
case "ERROR":
return OrderStatus.REJECTED;
case "INSUFFICIENT_BALANCE":
return OrderStatus.REJECTED;
default:
return null;
}
}
public static ExchangeMetaData adaptToExchangeMetaData(Map<String, BTCChinaTickerObject> products) {
Map<CurrencyPair, CurrencyPairMetaData> currencyPairs = new HashMap<>();
Map<Currency, CurrencyMetaData> currencies = new HashMap<>();
for (String product : products.keySet()) {
CurrencyPair pair = adaptCurrencyPair(product);
currencies.put(pair.base, null);
currencies.put(pair.counter, null);
}
return new ExchangeMetaData(currencyPairs, currencies, null, null, false);
}
}