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,144 @@ 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(Order.OrderType.BID.equals(limitOrder.getType())? BUY.toUpperCase() : SELL.toUpperCase())
.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(Order.OrderType.BID.equals(marketOrder.getType())? BUY.toUpperCase() : SELL.toUpperCase())
scuevas-bc marked this conversation as resolved.
Show resolved Hide resolved
.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(Order.OrderType.BID.equals(stopOrder.getType())? BUY.toUpperCase() : SELL.toUpperCase())
.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(), null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why null?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be the currencyMetadata CurrencyMetaData(Integer scale, BigDecimal withdrawalFee) of which we only have the scale and no other data .... so I left it in null.
But I can build the currency with only the scale.

}

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

return new ExchangeMetaData(currencyPairs, currency, rateLimits, rateLimits, false);
}
}
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,77 @@ 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 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