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

[Blockchain] marketDataService implementation #4

Open
wants to merge 3 commits into
base: bcdc-tradeservice-implementation
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package org.knowm.xchange.blockchain;

import org.knowm.xchange.blockchain.dto.BlockchainException;
import org.knowm.xchange.blockchain.dto.account.BlockchainSymbol;
import org.knowm.xchange.blockchain.dto.marketdata.BlockchainOrderBook;

import javax.ws.rs.*;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Map;

/**
Expand All @@ -23,4 +29,15 @@ public interface Blockchain {
@Path("/symbols")
@GET
Map<String, BlockchainSymbol> getSymbols();

/**
* Level 3 Order Book data is available through the l3 channel. Each entry in bids and asks arrays is an order,

Choose a reason for hiding this comment

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

an order or in order?

* along with its id (id), price (px) and quantity (qty) attributes.
*
* @param symbol
* @return All individual orders without aggregation of the L3 order book.
*/
@Path("/l3/{symbol}")
@GET
BlockchainOrderBook getOrderBookL3(@PathParam("symbol") String symbol) throws IOException, BlockchainException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
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.marketdata.BlockchainMarketDataOrder;
import org.knowm.xchange.blockchain.dto.marketdata.BlockchainOrderBook;
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.OrderBook;
import org.knowm.xchange.dto.marketdata.Trade;
import org.knowm.xchange.dto.marketdata.Trades;
import org.knowm.xchange.dto.meta.CurrencyMetaData;
import org.knowm.xchange.dto.meta.CurrencyPairMetaData;
Expand Down Expand Up @@ -249,6 +253,41 @@ public static ExchangeMetaData adaptMetaData(Map<String, BlockchainSymbol> marke
return new ExchangeMetaData(currencyPairs, currency, rateLimits, rateLimits, false);
}

public static OrderBook toOrderBook(BlockchainOrderBook blockchainOrderBook) {
List<LimitOrder> asks = blockchainOrderBook.getAsks().stream()
.map(limitOrder -> toLimitOrder(limitOrder, Order.OrderType.ASK, blockchainOrderBook.getSymbol()))
.collect(Collectors.toList());
List<LimitOrder> bids = blockchainOrderBook.getAsks().stream()
.map(limitOrder -> toLimitOrder(limitOrder, Order.OrderType.BID, blockchainOrderBook.getSymbol()))
.collect(Collectors.toList());

return new OrderBook(null, asks, bids);
}

public static LimitOrder toLimitOrder(BlockchainMarketDataOrder blockchainMarketDataOrder, Order.OrderType orderType , CurrencyPair currencyPair) {
return new LimitOrder.Builder(orderType, currencyPair)
.instrument(currencyPair)
.limitPrice(blockchainMarketDataOrder.getPrice())
.originalAmount(blockchainMarketDataOrder.getQuantity())
.build();
}

public static Trades toTrades(List<BlockchainOrder> blockchainTrades, CurrencyPair currencyPair) {
List<Trade> trades = blockchainTrades.stream()
.map(blockchainTrade -> {
Order.OrderType orderType = blockchainTrade.isBuyer() ? Order.OrderType.BID : Order.OrderType.ASK;
return new Trade.Builder()
.instrument(currencyPair)
.type(orderType)
.originalAmount(blockchainTrade.getCumQty())
.price(blockchainTrade.getPrice())
.timestamp(blockchainTrade.getTimestamp())
.id(blockchainTrade.getClOrdId())
.build();
}).collect(Collectors.toList());
return new Trades(trades, Trades.TradeSortType.SortByTimestamp);
}

public static String getOrderType(Order.OrderType type){
return Order.OrderType.BID.equals(type)? BUY.toUpperCase() : SELL.toUpperCase();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,6 @@ List<BlockchainDeposits> depositHistory(@QueryParam("from") Long startTime,
List<BlockchainOrder> getTrades(@QueryParam("symbol") String symbol,
@QueryParam("from") Long startTime,
@QueryParam("to") Long endTime,
@QueryParam("limit") Integer limit);
@QueryParam("limit") Integer limit) throws IOException, BlockchainException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class BlockchainConstants {
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 GET_ORDER_BOOK_L3 = "getOrderBookL3";
public static final String GET_EXCHANGE_TRADES = "getExchangeTrades";
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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public static ExchangeException adapt(BlockchainException e) {
return new ExchangeSecurityException(message, e);
case 404:
return new RateLimitExceededException(message, e);
case 500:
return new InternalServerException(message, e);
default:
return new ExchangeException(message, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import org.knowm.xchange.blockchain.dto.account.BlockchainSymbol;
import org.knowm.xchange.blockchain.service.BlockchainAccountService;
import org.knowm.xchange.blockchain.service.BlockchainAccountServiceRaw;
import org.knowm.xchange.blockchain.service.BlockchainMarketDataService;
import org.knowm.xchange.blockchain.service.BlockchainTradeService;
import org.knowm.xchange.client.ExchangeRestProxyBuilder;
import org.knowm.xchange.client.ResilienceRegistries;
import org.knowm.xchange.exceptions.ExchangeException;
import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException;
import org.knowm.xchange.service.marketdata.MarketDataService;
import si.mazi.rescu.SynchronizedValueFactory;

import javax.ws.rs.HeaderParam;
Expand Down Expand Up @@ -39,6 +39,7 @@ protected void initServices() {

this.accountService = new BlockchainAccountService(this, this.blockchain, this.getResilienceRegistries());
this.tradeService = new BlockchainTradeService(this, this.blockchain, this.getResilienceRegistries());
this.marketDataService = new BlockchainMarketDataService(this, this.blockchain, this.getResilienceRegistries());
}

@Override
Expand Down Expand Up @@ -72,9 +73,4 @@ public ResilienceRegistries getResilienceRegistries() {
}
return RESILIENCE_REGISTRIES;
}

@Override
public MarketDataService getMarketDataService() {
throw new NotYetImplementedForExchangeException(NOT_IMPLEMENTED_YET);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.knowm.xchange.blockchain.dto.marketdata;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import lombok.extern.jackson.Jacksonized;

import java.math.BigDecimal;

@Data
@Builder
@Jacksonized
@JsonIgnoreProperties(ignoreUnknown = true)
public class BlockchainMarketDataOrder {

@JsonProperty("px")
private final BigDecimal price;
@JsonProperty("qty")
private final BigDecimal quantity;
private final Long num;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.knowm.xchange.blockchain.dto.marketdata;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Builder;
import lombok.Data;
import lombok.extern.jackson.Jacksonized;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.utils.jackson.CurrencyPairDeserializer;

import java.util.List;

@Data
@Builder
@Jacksonized
@JsonIgnoreProperties(ignoreUnknown = true)
public class BlockchainOrderBook {

@JsonDeserialize(using = CurrencyPairDeserializer.class)
private final CurrencyPair symbol;
private final List<BlockchainMarketDataOrder> bids;
private final List<BlockchainMarketDataOrder> asks;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.knowm.xchange.blockchain.service;

import org.knowm.xchange.blockchain.BlockchainAdapters;
import org.knowm.xchange.blockchain.BlockchainAuthenticated;
import org.knowm.xchange.blockchain.BlockchainErrorAdapter;
import org.knowm.xchange.blockchain.BlockchainExchange;
import org.knowm.xchange.blockchain.dto.BlockchainException;
import org.knowm.xchange.client.ResilienceRegistries;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.marketdata.OrderBook;
import org.knowm.xchange.dto.marketdata.Ticker;
import org.knowm.xchange.dto.marketdata.Trades;
import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException;
import org.knowm.xchange.instrument.Instrument;
import org.knowm.xchange.service.marketdata.MarketDataService;
import org.knowm.xchange.service.marketdata.params.Params;

import java.io.IOException;
import java.util.List;

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

public class BlockchainMarketDataService extends BlockchainMarketDataServiceRaw implements MarketDataService {

public BlockchainMarketDataService(BlockchainExchange exchange, BlockchainAuthenticated blockchainApi, ResilienceRegistries resilienceRegistries) {
super(exchange, blockchainApi, resilienceRegistries);
}

@Override
public Ticker getTicker(CurrencyPair currencyPair, Object... args) throws IOException {
throw new NotYetImplementedForExchangeException(NOT_IMPLEMENTED_YET);
}

@Override
public Ticker getTicker(Instrument instrument, Object... args) throws IOException {
throw new NotYetImplementedForExchangeException(NOT_IMPLEMENTED_YET);
}

@Override
public List<Ticker> getTickers(Params params) {
throw new NotYetImplementedForExchangeException(NOT_IMPLEMENTED_YET);
}

@Override
public OrderBook getOrderBook(CurrencyPair currencyPair, Object... args) throws IOException {
try {
return BlockchainAdapters.toOrderBook(this.getOrderBookL3(currencyPair));
} catch (BlockchainException e) {
throw BlockchainErrorAdapter.adapt(e);
}
}

@Override
public OrderBook getOrderBook(Instrument instrument, Object... args) throws IOException {
try {
return BlockchainAdapters.toOrderBook(this.getOrderBookL3(BlockchainAdapters.toCurrencyPair(instrument)));
} catch (BlockchainException e) {
throw BlockchainErrorAdapter.adapt(e);
}
}

@Override
public Trades getTrades(CurrencyPair currencyPair, Object... args) throws IOException {
try {
Long startTime = null;
Long endTime = null;
Integer limit = null;
String symbol = BlockchainAdapters.toSymbol(currencyPair);

if (args != null) {
switch (args.length) {
case 2:
if (args[1] != null && args[1] instanceof Long) {

Choose a reason for hiding this comment

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

I would create constants for these numbers... ZERO, ONE.. or even make them more self-declarative like START_DATE_INDEX, END_TIME_INDEX

endTime = (Long) args[1];
}
case 1:
if (args[0] != null && args[0] instanceof Long) {
startTime = (Long) args[0];
}
}
}

return BlockchainAdapters.toTrades(
this.getExchangeTrades(symbol, startTime, endTime, limit),
currencyPair
);
}catch (BlockchainException e){
throw BlockchainErrorAdapter.adapt(e);
}
}

@Override
public Trades getTrades(Instrument instrument, Object... args) throws IOException {
CurrencyPair currencyPair = BlockchainAdapters.toCurrencyPair(instrument);
return getTrades(currencyPair,args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.knowm.xchange.blockchain.service;

import org.knowm.xchange.blockchain.BlockchainAdapters;
import org.knowm.xchange.blockchain.BlockchainAuthenticated;
import org.knowm.xchange.blockchain.BlockchainExchange;
import org.knowm.xchange.blockchain.dto.BlockchainException;
import org.knowm.xchange.blockchain.dto.marketdata.BlockchainOrderBook;
import org.knowm.xchange.blockchain.dto.trade.BlockchainOrder;
import org.knowm.xchange.client.ResilienceRegistries;
import org.knowm.xchange.currency.CurrencyPair;

import java.io.IOException;
import java.util.List;

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

public class BlockchainMarketDataServiceRaw extends BlockchainBaseService{

protected BlockchainMarketDataServiceRaw(BlockchainExchange exchange, BlockchainAuthenticated blockchainApi, ResilienceRegistries resilienceRegistries) {
super(exchange, blockchainApi, resilienceRegistries);
}

protected BlockchainOrderBook getOrderBookL3(CurrencyPair currencyPair) throws IOException, BlockchainException {
return decorateApiCall(() -> this.blockchainApi.getOrderBookL3(BlockchainAdapters.toSymbol(currencyPair)))
.withRetry(retry(GET_ORDER_BOOK_L3))
.withRateLimiter(rateLimiter(ENDPOINT_RATE_LIMIT))
.call();
}

protected List<BlockchainOrder> getExchangeTrades(String symbol, Long startTime, Long endTime, Integer limit) throws IOException, BlockchainException {
return decorateApiCall(() -> this.blockchainApi.getTrades(symbol, startTime, endTime, limit))
.withRetry(retry(GET_EXCHANGE_TRADES))
.withRateLimiter(rateLimiter(ENDPOINT_RATE_LIMIT))
.call();
}
}
Loading