Skip to content
This repository was archived by the owner on May 15, 2020. It is now read-only.

Commit d8fedbc

Browse files
Start to standardise these three exchanges around the common API defined in bitrich-info#274.
Coinbase Pro is a challenge here. It seems to need an initial snapshot. At the moment the implementation is bare-bones. Need to start working on the ordering challenge. All three seem to provide a sequence number. This will be essential for maintaining any sort of snapshot. Not done balances yet.
1 parent 6d639b8 commit d8fedbc

File tree

19 files changed

+768
-414
lines changed

19 files changed

+768
-414
lines changed

xchange-binance/src/main/java/info/bitrich/xchangestream/binance/BinanceStreamingAdapters.java

-20
This file was deleted.

xchange-binance/src/main/java/info/bitrich/xchangestream/binance/BinanceStreamingMarketDataService.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import info.bitrich.xchangestream.binance.dto.ExecutionReportBinanceUserTransaction.ExecutionType;
1414
import info.bitrich.xchangestream.binance.dto.TickerBinanceWebsocketTransaction;
1515
import info.bitrich.xchangestream.binance.dto.TradeBinanceWebsocketTransaction;
16-
import info.bitrich.xchangestream.core.OrderStatusChange;
1716
import info.bitrich.xchangestream.core.ProductSubscription;
1817
import info.bitrich.xchangestream.core.StreamingMarketDataService;
1918
import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper;
@@ -28,6 +27,7 @@
2827
import org.knowm.xchange.binance.dto.marketdata.BinanceTicker24h;
2928
import org.knowm.xchange.binance.service.BinanceMarketDataService;
3029
import org.knowm.xchange.currency.CurrencyPair;
30+
import org.knowm.xchange.dto.Order;
3131
import org.knowm.xchange.dto.Order.OrderType;
3232
import org.knowm.xchange.dto.marketdata.OrderBook;
3333
import org.knowm.xchange.dto.marketdata.OrderBookUpdate;
@@ -126,13 +126,10 @@ public Observable<Trade> getTrades(CurrencyPair currencyPair, Object... args) {
126126
}
127127

128128
@Override
129-
public Observable<OrderStatusChange> getOrderStatusChanges(CurrencyPair currencyPair, Object... args) {
129+
public Observable<Order> getOrderChanges(CurrencyPair currencyPair, Object... args) {
130130
return getRawExecutionReports()
131131
.filter(r -> currencyPair.equals(r.getCurrencyPair()))
132-
.filter(r -> r.getExecutionType().equals(ExecutionType.CANCELED) ||
133-
r.getExecutionType().equals(ExecutionType.EXPIRED) ||
134-
r.getExecutionType().equals(ExecutionType.NEW))
135-
.map(BinanceStreamingAdapters::adaptOrderStatusChange);
132+
.map(ExecutionReportBinanceUserTransaction::toOrder);
136133
}
137134

138135
public Observable<UserTrade> getUserTrades() {
@@ -141,6 +138,7 @@ public Observable<UserTrade> getUserTrades() {
141138
.map(ExecutionReportBinanceUserTransaction::toUserTrade);
142139
}
143140

141+
@Override
144142
public Observable<UserTrade> getUserTrades(CurrencyPair currencyPair, Object... args) {
145143
return getUserTrades().filter(t -> t.getCurrencyPair().equals(currencyPair));
146144
}

xchange-binance/src/main/java/info/bitrich/xchangestream/binance/dto/ExecutionReportBinanceUserTransaction.java

+53-17
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,34 @@
22

33
import com.fasterxml.jackson.annotation.JsonProperty;
44

5-
import java.math.BigDecimal;
6-
75
import org.knowm.xchange.binance.BinanceAdapters;
6+
import org.knowm.xchange.binance.dto.trade.BinanceOrder;
7+
import org.knowm.xchange.binance.dto.trade.OrderSide;
8+
import org.knowm.xchange.binance.dto.trade.OrderStatus;
9+
import org.knowm.xchange.binance.dto.trade.OrderType;
10+
import org.knowm.xchange.binance.dto.trade.TimeInForce;
811
import org.knowm.xchange.currency.Currency;
12+
import org.knowm.xchange.dto.Order;
913
import org.knowm.xchange.dto.trade.UserTrade;
1014

15+
import java.math.BigDecimal;
16+
1117
public class ExecutionReportBinanceUserTransaction extends ProductBinanceWebSocketTransaction {
1218

1319
public enum ExecutionType {
1420
NEW, CANCELED, REPLACED, REJECTED, TRADE, EXPIRED
1521
}
1622

1723
private final String clientOrderId;
18-
private final String side;
19-
private final String orderType;
20-
private final String timeInForce;
24+
private final OrderSide side;
25+
private final OrderType orderType;
26+
private final TimeInForce timeInForce;
2127
private final BigDecimal orderQuantity;
2228
private final BigDecimal orderPrice;
2329
private final BigDecimal stopPrice;
2430
private final BigDecimal icebergQuantity;
2531
private final ExecutionType executionType;
26-
private final String currentOrderStatus;
32+
private final OrderStatus currentOrderStatus;
2733
private final String orderRejectReason;
2834
private final long orderId;
2935
private final BigDecimal lastExecutedQuantity;
@@ -66,15 +72,15 @@ public ExecutionReportBinanceUserTransaction(
6672
{
6773
super(eventType, eventTime, symbol);
6874
this.clientOrderId = clientOrderId;
69-
this.side = side;
70-
this.orderType = orderType;
71-
this.timeInForce = timeInForce;
75+
this.side = OrderSide.valueOf(side);
76+
this.orderType = OrderType.valueOf(orderType);
77+
this.timeInForce = TimeInForce.valueOf(timeInForce);
7278
this.orderQuantity = quantity;
7379
this.orderPrice = price;
7480
this.stopPrice = stopPrice;
7581
this.icebergQuantity = icebergQuantity;
7682
this.executionType = ExecutionType.valueOf(currentExecutionType);
77-
this.currentOrderStatus = currentOrderStatus;
83+
this.currentOrderStatus = OrderStatus.valueOf(currentOrderStatus);
7884
this.orderRejectReason = orderRejectReason;
7985
this.orderId = orderId;
8086
this.lastExecutedQuantity = lastExecutedQuantity;
@@ -93,15 +99,15 @@ public String getClientOrderId() {
9399
return clientOrderId;
94100
}
95101

96-
public String getSide() {
102+
public OrderSide getSide() {
97103
return side;
98104
}
99105

100-
public String getOrderType() {
106+
public OrderType getOrderType() {
101107
return orderType;
102108
}
103109

104-
public String getTimeInForce() {
110+
public TimeInForce getTimeInForce() {
105111
return timeInForce;
106112
}
107113

@@ -125,7 +131,7 @@ public ExecutionType getExecutionType() {
125131
return executionType;
126132
}
127133

128-
public String getCurrentOrderStatus() {
134+
public OrderStatus getCurrentOrderStatus() {
129135
return currentOrderStatus;
130136
}
131137

@@ -178,9 +184,39 @@ public BigDecimal getCumulativeQuoteAssetTransactedQuantity() {
178184
}
179185

180186
public UserTrade toUserTrade() {
181-
return new UserTrade(BinanceAdapters.convertType(buyerMarketMaker), lastExecutedQuantity, currencyPair,
182-
lastExecutedPrice, getEventTime(), Long.toString(tradeId), Long.toString(orderId), commissionAmount,
183-
Currency.getInstance(commissionAsset));
187+
if (executionType != ExecutionType.TRADE)
188+
throw new IllegalStateException("Not a trade");
189+
return new UserTrade.Builder()
190+
.type(BinanceAdapters.convertType(buyerMarketMaker))
191+
.originalAmount(lastExecutedQuantity)
192+
.currencyPair(currencyPair)
193+
.price(lastExecutedPrice)
194+
.timestamp(getEventTime())
195+
.id(Long.toString(tradeId))
196+
.orderId(Long.toString(orderId))
197+
.feeAmount(commissionAmount)
198+
.feeCurrency(Currency.getInstance(commissionAsset))
199+
.build();
200+
}
201+
202+
public Order toOrder() {
203+
return BinanceAdapters.adaptOrder(
204+
new BinanceOrder(
205+
BinanceAdapters.toSymbol(getCurrencyPair()),
206+
orderId,
207+
clientOrderId,
208+
orderPrice,
209+
orderQuantity,
210+
cumulativeFilledQuantity,
211+
currentOrderStatus,
212+
timeInForce,
213+
orderType,
214+
side,
215+
stopPrice,
216+
BigDecimal.ZERO,
217+
timestamp
218+
)
219+
);
184220
}
185221

186222
@Override

xchange-bitfinex/src/main/java/info/bitrich/xchangestream/bitfinex/BitfinexStreamingAdapters.java

+81-22
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@
66
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthOrder;
77
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthPreTrade;
88
import info.bitrich.xchangestream.bitfinex.dto.BitfinexWebSocketAuthTrade;
9-
import info.bitrich.xchangestream.core.OrderStatusChange;
10-
import info.bitrich.xchangestream.core.OrderStatusChangeType;
11-
12-
import io.reactivex.annotations.Nullable;
139

1410
import io.reactivex.annotations.Nullable;
1511

12+
import org.knowm.xchange.bitfinex.v1.BitfinexAdapters;
13+
import org.knowm.xchange.bitfinex.v1.BitfinexOrderType;
14+
import org.knowm.xchange.bitfinex.v1.dto.trade.BitfinexOrderStatusResponse;
15+
import org.knowm.xchange.currency.Currency;
16+
import org.knowm.xchange.dto.Order;
17+
import org.knowm.xchange.dto.Order.OrderType;
18+
import org.knowm.xchange.dto.trade.OpenOrders;
19+
import org.knowm.xchange.dto.trade.UserTrade;
20+
import org.knowm.xchange.utils.DateUtils;
1621
import org.slf4j.Logger;
1722
import org.slf4j.LoggerFactory;
1823

1924
import java.math.BigDecimal;
20-
import java.util.Date;
2125
import java.util.stream.Stream;
2226

2327
import static java.util.stream.StreamSupport.stream;
@@ -26,6 +30,9 @@ class BitfinexStreamingAdapters {
2630

2731
private static final Logger LOG = LoggerFactory.getLogger(BitfinexStreamingAdapters.class);
2832

33+
private static final BigDecimal THOUSAND = new BigDecimal(1000);
34+
35+
2936
@Nullable
3037
static BitfinexWebSocketAuthPreTrade adaptPreTrade(JsonNode preTrade) {
3138
if (preTrade.size() < 12) {
@@ -155,25 +162,77 @@ static private BitfinexWebSocketAuthOrder createOrderObject(JsonNode order) {
155162
);
156163
}
157164

158-
@Nullable
159-
static OrderStatusChange adaptOrderStatus(BitfinexWebSocketAuthOrder authOrder) {
160-
OrderStatusChangeType status = adaptOrderStatusType(authOrder.getOrderStatus());
161-
if (status == null)
162-
return OrderStatusChange.create().build();
163-
return OrderStatusChange.create()
164-
.type(status)
165-
.timestamp(new Date())
166-
.orderId(Long.toString(authOrder.getId()))
167-
.build();
165+
private static BitfinexOrderType adaptV2OrderTypeToV1(String orderType) {
166+
switch(orderType) {
167+
case "LIMIT": return BitfinexOrderType.MARGIN_LIMIT;
168+
case "MARKET": return BitfinexOrderType.MARGIN_MARKET;
169+
case "STOP": return BitfinexOrderType.MARGIN_STOP;
170+
case "TRAILING STOP": return BitfinexOrderType.MARGIN_TRAILING_STOP;
171+
case "EXCHANGE MARKET": return BitfinexOrderType.MARKET;
172+
case "EXCHANGE LIMIT": return BitfinexOrderType.LIMIT;
173+
case "EXCHANGE STOP": return BitfinexOrderType.STOP;
174+
case "EXCHANGE TRAILING STOP": return BitfinexOrderType.TRAILING_STOP;
175+
case "FOK": return BitfinexOrderType.MARGIN_FILL_OR_KILL;
176+
case "EXCHANGE FOK": return BitfinexOrderType.FILL_OR_KILL;
177+
default: return BitfinexOrderType.LIMIT; // Safe fallback
178+
}
168179
}
169180

170-
@Nullable
171-
private static OrderStatusChangeType adaptOrderStatusType(String orderStatus) {
172-
switch(orderStatus) {
173-
case "ACTIVE": return OrderStatusChangeType.OPENED;
174-
case "EXECUTED": return OrderStatusChangeType.CLOSED;
175-
case "CANCELED": return OrderStatusChangeType.CLOSED;
176-
default: return null;
181+
private static String adaptV2SymbolToV1(String symbol) {
182+
return symbol.substring(1);
183+
}
184+
185+
/**
186+
* We adapt the websocket message to what we expect from the V1 REST API, so that
187+
* we don't re-implement the complex logic which works out whether we need
188+
* limit orders, stop orders, market orders etc.
189+
*/
190+
private static BitfinexOrderStatusResponse adaptOrderToRestResponse(BitfinexWebSocketAuthOrder authOrder) {
191+
int signum = authOrder.getAmountOrig().signum();
192+
return new BitfinexOrderStatusResponse(
193+
authOrder.getId(),
194+
adaptV2SymbolToV1(authOrder.getSymbol()),
195+
authOrder.getPrice(),
196+
authOrder.getPriceAvg(),
197+
signum > 0 ? "buy" : "sell",
198+
adaptV2OrderTypeToV1(authOrder.getType()).getValue(),
199+
new BigDecimal(authOrder.getMtsCreate()).divide(THOUSAND),
200+
"ACTIVE".equals(authOrder.getOrderStatus()),
201+
"CANCELED".equals(authOrder.getOrderStatus()),
202+
false, //wasForced,
203+
signum >= 0 ? authOrder.getAmountOrig() : authOrder.getAmountOrig().negate(),
204+
signum >= 0 ? authOrder.getAmount() : authOrder.getAmount().negate(),
205+
signum >= 0
206+
? authOrder.getAmountOrig().subtract(authOrder.getAmount())
207+
: authOrder.getAmountOrig().subtract(authOrder.getAmount()).negate()
208+
);
209+
}
210+
211+
static Order adaptOrder(BitfinexWebSocketAuthOrder authOrder) {
212+
BitfinexOrderStatusResponse[] orderStatus = { adaptOrderToRestResponse(authOrder)};
213+
OpenOrders orders = BitfinexAdapters.adaptOrders(orderStatus);
214+
if (orders.getOpenOrders().isEmpty()) {
215+
if (orders.getHiddenOrders().isEmpty()) {
216+
throw new IllegalStateException("No order in message");
217+
}
218+
return orders.getHiddenOrders().get(0);
177219
}
220+
return orders.getOpenOrders().get(0);
221+
}
222+
223+
static UserTrade adaptUserTrade(BitfinexWebSocketAuthTrade authTrade) {
224+
return new UserTrade.Builder()
225+
.currencyPair(BitfinexAdapters.adaptCurrencyPair(adaptV2SymbolToV1(authTrade.getPair())))
226+
.feeAmount(authTrade.getFee())
227+
.feeCurrency(Currency.getInstance(authTrade.getFeeCurrency()))
228+
.id(Long.toString(authTrade.getId()))
229+
.orderId(Long.toString(authTrade.getOrderId()))
230+
.originalAmount(authTrade.getExecAmount())
231+
.price(authTrade.getExecPrice())
232+
.timestamp(DateUtils.fromMillisUtc(authTrade.getMtsCreate()))
233+
.type(authTrade.getOrderType().equalsIgnoreCase("buy")
234+
? OrderType.BID
235+
: OrderType.ASK)
236+
.build();
178237
}
179238
}

0 commit comments

Comments
 (0)