Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

tradeservice implementation #2

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
package org.knowm.xchange.blockchain;

import lombok.experimental.UtilityClass;
import org.knowm.xchange.blockchain.dto.account.*;
import org.knowm.xchange.blockchain.dto.account.BlockchainDeposit;
import org.knowm.xchange.blockchain.dto.account.BlockchainDeposits;
import org.knowm.xchange.blockchain.dto.account.BlockchainSymbol;
import org.knowm.xchange.blockchain.dto.account.BlockchainWithdrawal;
import org.knowm.xchange.blockchain.dto.trade.BlockchainOrder;
import org.knowm.xchange.currency.Currency;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.Order;
import org.knowm.xchange.dto.account.AddressWithTag;
import org.knowm.xchange.dto.account.FundingRecord;
import org.knowm.xchange.dto.marketdata.Trades;
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.meta.RateLimit;
import org.knowm.xchange.dto.trade.*;
import org.knowm.xchange.instrument.Instrument;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.knowm.xchange.blockchain.BlockchainConstants.*;

Expand All @@ -18,6 +33,13 @@ public static String toSymbol(CurrencyPair currencyPair) {
return String.format(CURRENCY_PAIR_SYMBOL_FORMAT, currencyPair.base.getCurrencyCode(), currencyPair.counter.getCurrencyCode());
}

public static CurrencyPair toCurrencyPair(Instrument instrument){
if(instrument instanceof CurrencyPair) {
return (CurrencyPair) instrument;
}
throw new IllegalArgumentException(String.format("Unsupported instrument '%s'", instrument));
}

public static AddressWithTag toAddressWithTag(BlockchainDeposit blockchainDeposit){
return new AddressWithTag(blockchainDeposit.getAddress(), null);
}
Expand Down Expand Up @@ -86,4 +108,148 @@ public static CurrencyPair toCurrencyPairBySymbol(BlockchainSymbol blockchainSym
Currency counterSymbol = blockchainSymbol.getCounterCurrency();
return new CurrencyPair(baseSymbol, counterSymbol);
}

public static OpenOrders toOpenOrders(List<BlockchainOrder> blockchainOrders){
List<LimitOrder> limitOrders = new ArrayList<>();
List<Order> hiddenOrders = new ArrayList<>();

for(BlockchainOrder blockchainOrder : blockchainOrders) {
Order.Builder builder = blockchainOrder.getOrderBuilder();

Order order = builder.orderStatus(toOrderStatus(blockchainOrder.getOrdStatus()))
.originalAmount(blockchainOrder.getCumQty().add(blockchainOrder.getLeavesQty()))
.id(Long.toString(blockchainOrder.getExOrdId()))
.timestamp(blockchainOrder.getTimestamp())
.averagePrice(blockchainOrder.getAvgPx())
.build();

if (order instanceof LimitOrder) {
limitOrders.add((LimitOrder) order);
} else {
hiddenOrders.add(order);
}
}

return new OpenOrders(limitOrders, hiddenOrders);
}

public static Order toOpenOrdersById(BlockchainOrder blockchainOrder){
Order.Builder builder = blockchainOrder.getOrderBuilder();

return builder.originalAmount(blockchainOrder.getCumQty().add(blockchainOrder.getLeavesQty()))
.id(Long.toString(blockchainOrder.getExOrdId()))
.timestamp(blockchainOrder.getTimestamp())
.averagePrice(blockchainOrder.getAvgPx())
.cumulativeAmount(blockchainOrder.getCumQty())
.orderStatus(toOrderStatus(blockchainOrder.getOrdStatus()))
.userReference(blockchainOrder.getClOrdId())
.build();
}

public static Order.OrderStatus toOrderStatus(String status) {
switch (status.toUpperCase()) {
case OPEN:
return Order.OrderStatus.OPEN;
case REJECTED:
return Order.OrderStatus.REJECTED;
case CANCELED:
return Order.OrderStatus.CANCELED;
case FILLED:
return Order.OrderStatus.FILLED;
case PART_FILLED:
return Order.OrderStatus.PARTIALLY_FILLED;
case EXPIRED:
return Order.OrderStatus.EXPIRED;
case PENDING:
return Order.OrderStatus.PENDING_NEW;
default:
return Order.OrderStatus.UNKNOWN;
}
}

public static BlockchainOrder toBlockchainLimitOrder(LimitOrder limitOrder){
return BlockchainOrder.builder()
.ordType(LIMIT)
.symbol(toCurrencyPair(limitOrder.getInstrument()))
.side(getOrderType(limitOrder.getType()))
.orderQty(limitOrder.getOriginalAmount())
.price(limitOrder.getLimitPrice())
.clOrdId(generateClOrdId())
.build();
}

public static BlockchainOrder toBlockchainMarketOrder(MarketOrder marketOrder){
return BlockchainOrder.builder()
.ordType(MARKET)
.symbol(toCurrencyPair(marketOrder.getInstrument()))
.side(getOrderType(marketOrder.getType()))
.orderQty(marketOrder.getOriginalAmount())
.price(marketOrder.getCumulativeAmount())
.clOrdId(generateClOrdId())
.build();
}

public static BlockchainOrder toBlockchainStopOrder(StopOrder stopOrder){
return BlockchainOrder.builder()
.ordType(STOP)
.symbol(toCurrencyPair(stopOrder.getInstrument()))
.side(getOrderType(stopOrder.getType()))
.orderQty(stopOrder.getOriginalAmount())
.price(stopOrder.getLimitPrice())
.stopPx(stopOrder.getStopPrice())
.clOrdId(generateClOrdId())
.build();
}

private static String generateClOrdId() {
String uuid = UUID.randomUUID().toString();
uuid = uuid.substring(0, 16).replace("-", "");
return uuid;
}

public static UserTrades toUserTrades(List<BlockchainOrder> blockchainTrades) {
List<UserTrade> trades = blockchainTrades.stream()
.map(blockchainTrade -> new UserTrade.Builder()
.type(blockchainTrade.getOrderType())
.originalAmount(blockchainTrade.getCumQty())
.currencyPair(blockchainTrade.getSymbol())
.price(blockchainTrade.getPrice())
.timestamp(blockchainTrade.getTimestamp())
.id(Long.toString(blockchainTrade.getExOrdId()))
.orderId(blockchainTrade.getClOrdId())
.build()
).collect(Collectors.toList());
Long lastId = blockchainTrades.stream().map(BlockchainOrder::getExOrdId).max(Long::compareTo).orElse(0L);
return new UserTrades(trades, lastId, Trades.TradeSortType.SortByTimestamp);
}

public static ExchangeMetaData adaptMetaData(Map<String, BlockchainSymbol> markets) {
Map<CurrencyPair, CurrencyPairMetaData> currencyPairs = new HashMap<>();
Map<Currency, CurrencyMetaData> currency = new HashMap<>();

for (Map.Entry<String, BlockchainSymbol> entry : markets.entrySet()) {
CurrencyPair pair = BlockchainAdapters.toCurrencyPairBySymbol(entry.getValue());
BigDecimal minScale = BigDecimal.valueOf(Math.pow(10, (entry.getValue().getMinOrderSizeScale())*-1));
BigDecimal minAmount = entry.getValue().getMinOrderSize().multiply(minScale);
BigDecimal maxScale = BigDecimal.valueOf(Math.pow(10, (entry.getValue().getMaxOrderSizeScale())*-1));
BigDecimal maxAmount = entry.getValue().getMaxOrderSize().multiply(maxScale);
CurrencyPairMetaData currencyPairMetaData =
new CurrencyPairMetaData.Builder()
.baseScale(entry.getValue().getBaseCurrencyScale())
.priceScale(entry.getValue().getCounterCurrencyScale())
.minimumAmount(minAmount)
.maximumAmount(maxAmount)
.build();
currencyPairs.put(pair, currencyPairMetaData);
currency.put(entry.getValue().getBaseCurrency(), new CurrencyMetaData(entry.getValue().getBaseCurrencyScale(),null));
}

RateLimit[] rateLimits = {new RateLimit(30, 1, TimeUnit.SECONDS)};

return new ExchangeMetaData(currencyPairs, currency, rateLimits, rateLimits, false);
}

public static String getOrderType(Order.OrderType type){
return Order.OrderType.BID.equals(type)? BUY.toUpperCase() : SELL.toUpperCase();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.knowm.xchange.blockchain.dto.BlockchainException;
import org.knowm.xchange.blockchain.dto.account.*;
import org.knowm.xchange.blockchain.dto.trade.BlockchainOrder;
import org.knowm.xchange.blockchain.params.BlockchainWithdrawalParams;

import javax.ws.rs.*;
Expand Down Expand Up @@ -85,4 +86,87 @@ List<BlockchainWithdrawal> getWithdrawFunds(@QueryParam("from") Long startTime,
@GET
List<BlockchainDeposits> depositHistory(@QueryParam("from") Long startTime,
@QueryParam("to") Long endTime);

/**
* Get a list orders
*
* @return live and historic orders, defaulting to live orders. Returns at most 100 results, use timestamp to
* paginate for further results
*/
@Path("/orders")
@GET
List<BlockchainOrder> getOrders();

/**
* Get a list orders by symbol
*
* @return live and historic orders, defaulting to live orders. Returns at most 100 results, use timestamp to
* paginate for further results
*/
@Path("/orders")
@GET
List<BlockchainOrder> getOrdersBySymbol(@QueryParam("symbol") String symbol) throws IOException, BlockchainException;

/**
* Get a specific order
*
* @param orderId
* @return the order according to the orderId, 404 if not found
*/
@Path("/orders/{orderId}")
@GET
BlockchainOrder getOrder(@PathParam("orderId") String orderId) throws IOException, BlockchainException;;

/**
* Add an order
*
* @param blockchainOrder
* @return a new order according to the provided parameters
*/
@Path("/orders")
@POST
@Consumes(MediaType.APPLICATION_JSON)
BlockchainOrder postOrder(BlockchainOrder blockchainOrder);

/**
* Delete a specific order
*
* @param orderId
* @return status 200 if it was successfully removed or 400 if there was an error
* @throws IOException
* @throws BlockchainException
*/
@Path("/orders/{orderId}")
@DELETE
Void cancelOrder(@PathParam("orderId") String orderId) throws IOException, BlockchainException;

/**
* Delete all open orders (of a symbol, if specified)
*
* @param symbol
* @return status 200 if it was successfully removed or 400 if there was an error
* @throws IOException
* @throws BlockchainException
*/
@Path("/orders")
@DELETE
Void cancelAllOrders(@QueryParam("symbol") String symbol) throws IOException, BlockchainException;

/**
* Get a list of filled orders
*
* @param symbol
* @param startTime
* @param endTime
* @param limit
* @return filled orders, including partial fills. Returns at most 100 results, use timestamp to paginate for
* further results
*/
@Path("/trades")
@GET
List<BlockchainOrder> getTrades(@QueryParam("symbol") String symbol,
@QueryParam("from") Long startTime,
@QueryParam("to") Long endTime,
@QueryParam("limit") Integer limit);

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,37 @@ public class BlockchainConstants {
public static final String GET_FEES = "getFees";
public static final String GET_DEPOSIT_HISTORY = "depositHistory";
public static final String GET_WITHDRAWAL_HISTORY = "withdrawHistory";
public static final String GET_ORDERS = "getOrders";
public static final String GET_ORDER = "getOrder";
public static final String POST_ORDER = "postOrder";
public static final String CANCEL_ORDER = "cancelOrder";
public static final String CANCEL_ALL_ORDERS = "cancelAllOrders";
public static final String GET_SYMBOLS = "getSymbols";
public static final String GET_TRADES = "getTrades";
public static final String CURRENCY_PAIR_SYMBOL_FORMAT = "%s-%s";
public static final String X_API_TOKEN = "X-API-Token";
public static final String XCHANGE = "XChange";
public static final String X_API_INTEGRATION = "X_API_INTEGRATION";
public static final String WITHDRAWAL_EXCEPTION = "Invalid WithdrawFundsParams parameter. Only DefaultWithdrawFundsParams is supported.";
public static final String EXCEPTION_MESSAGE = "Operation failed without any error message";
public static final String FUNDING_RECORD_TYPE_UNSUPPORTED = "Invalid FundingRecord parameter. Only DefaultWithdrawFundsParams is supported.";
public static final String CURRENCY_PAIR_EXCEPTION = "Invalid TradeHistoryParams type, it should be an instance of BlockchainTradeHistoryParams";
public static final String OPEN = "OPEN";
public static final String REJECTED = "REJECTED";
public static final String REFUNDING = "REFUNDING";
public static final String PENDING = "PENDING";
public static final String FAILED = "FAILED";
public static final String COMPLETED = "COMPLETED";
public static final String UNCONFIRMED = "UNCONFIRMED";
public static final String CANCELED = "CANCELED";
public static final String FILLED = "FILLED";
public static final String PART_FILLED = "PART_FILLED";
public static final String EXPIRED = "EXPIRED";
public static final String STATUS_INVALID = "Unknown withdraw status: ";
public static final String NOT_IMPLEMENTED_YET = "Not implemented yet";
public static final String MARKET = "MARKET";
public static final String LIMIT = "LIMIT";
public static final String STOP = "STOPLIMIT";
public static final String BUY = "buy";
public static final String SELL = "sell";
}
Loading