diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexAuthenticator.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexAuthenticator.java new file mode 100644 index 000000000..665c0b3c2 --- /dev/null +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexAuthenticator.java @@ -0,0 +1,38 @@ +package info.bitrich.xchangestream.bitmex; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; + +/** + * Created by heath on 2018/3/1. + */ +public class BitmexAuthenticator { + + public static String getSHA256String(String str, String key) { + + try { + Charset asciiCs = Charset.forName("US-ASCII"); + SecretKeySpec signingKey = new SecretKeySpec(asciiCs.encode(key).array(), "HmacSHA256"); + Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); + sha256_HMAC.init(signingKey); + byte[] mac_data = sha256_HMAC.doFinal(asciiCs.encode(str).array()); + StringBuilder result = new StringBuilder(); + for (final byte element : mac_data) { + result.append(Integer.toString((element & 0xff) + 0x100, 16).substring(1)); + } + // System.out.println("SHA256String Result:[" + result + "]"); + return result.toString().toUpperCase(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String generateSignature(String secret, String verb, String url, String nonce, String data) { + String message = verb + url + nonce + data; + // System.out.println(message); + return getSHA256String(message, secret); + } + +} diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingExchange.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingExchange.java index c9cf0ab8b..56096fb8b 100644 --- a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingExchange.java +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingExchange.java @@ -29,6 +29,7 @@ protected BitmexStreamingExchange(BitmexStreamingService streamingService) { protected void initServices() { super.initServices(); streamingMarketDataService = new BitmexStreamingMarketDataService(streamingService); + streamingService.setExchangeSpecification(this.getExchangeSpecification()); } @Override diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingMarketDataService.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingMarketDataService.java index bf97ac90b..45f2ec920 100644 --- a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingMarketDataService.java +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingMarketDataService.java @@ -89,4 +89,5 @@ public Observable getTrades(CurrencyPair currencyPair, Object... args) { return trades; }); } + } diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingService.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingService.java index e5dbc67f4..c1d7bbe10 100644 --- a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingService.java +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingService.java @@ -1,9 +1,12 @@ package info.bitrich.xchangestream.bitmex; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.reactivex.Completable; +import io.reactivex.CompletableSource; +import org.knowm.xchange.ExchangeSpecification; import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; import org.slf4j.Logger; @@ -25,12 +28,52 @@ public class BitmexStreamingService extends JsonNettyStreamingService { private static final Logger LOG = LoggerFactory.getLogger(BitmexStreamingService.class); private final ObjectMapper mapper = new ObjectMapper(); + protected ExchangeSpecification exchangeSpecification; + public BitmexStreamingService(String apiUrl) { super(apiUrl, Integer.MAX_VALUE); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } - @Override + public void setExchangeSpecification(ExchangeSpecification exchangeSpecification) { + this.exchangeSpecification = exchangeSpecification; + } + + private void login() throws JsonProcessingException { + long expires = System.currentTimeMillis() + 30; + String apiKey = this.exchangeSpecification.getApiKey(); + String apiSecret = this.exchangeSpecification.getSecretKey(); + String path = "/realtime"; + String signature = BitmexAuthenticator.generateSignature(apiSecret, + "GET", path, String.valueOf(expires), ""); + + List args = Arrays.asList(apiKey, expires, signature); + + Map cmd = new HashMap<>(); + cmd.put("op", "authKey"); + cmd.put("args", args); + this.sendMessage(mapper.writeValueAsString(cmd)); + } + + @Override + public Completable connect() { + // Note that we must override connect method in streaming service instead of streaming exchange, because of the auto reconnect feature of NettyStreamingService. + // We must ensure the authentication message is also resend when the connection is rebuilt. + Completable conn = super.connect(); + if (this.exchangeSpecification.getApiKey() == null) { + return conn; + } + return conn.andThen((CompletableSource)(completable) -> { + try { + login(); + completable.onComplete(); + } catch (IOException e) { + completable.onError(e); + } + }); + } + + @Override protected void handleMessage(JsonNode message) { if (message.has("info") || message.has("success")) { return; diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingTradeService.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingTradeService.java new file mode 100644 index 000000000..39d1630b3 --- /dev/null +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/BitmexStreamingTradeService.java @@ -0,0 +1,43 @@ +package info.bitrich.xchangestream.bitmex; + + +import info.bitrich.xchangestream.bitmex.dto.BitmexOrder; +import info.bitrich.xchangestream.core.StreamingTradeService; +import io.reactivex.Observable; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; + +import java.util.Arrays; +import java.util.stream.Collectors; + + +/** + * Created by Declan + */ +public class BitmexStreamingTradeService implements StreamingTradeService { + + private final BitmexStreamingService streamingService; + + public BitmexStreamingTradeService(BitmexStreamingService streamingService) { + this.streamingService = streamingService; + } + + @Override + public Observable getOrders(CurrencyPair currencyPair, Object... args) { + String channelName = "order"; + String instrument = currencyPair.base.toString() + currencyPair.counter.toString(); + return streamingService.subscribeBitmexChannel(channelName).flatMapIterable(s -> { + BitmexOrder[] bitmexOrders = s.toBitmexOrders(); + return Arrays.stream(bitmexOrders) + .filter(bitmexOrder -> bitmexOrder.getSymbol() == instrument) + .filter(BitmexOrder::isNotWorkingIndicator) + .map(BitmexOrder::toOrder).collect(Collectors.toList()); + }); + } + + @Override + public void submitOrder(Order order, CurrencyPair var1, Object... var2) { + throw new NotYetImplementedForExchangeException(); + } +} diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexOrder.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexOrder.java new file mode 100644 index 000000000..e17a60d4d --- /dev/null +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexOrder.java @@ -0,0 +1,125 @@ +package info.bitrich.xchangestream.bitmex.dto; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.knowm.xchange.bitmex.BitmexUtils; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.dto.trade.MarketOrder; + + +import java.math.BigDecimal; + + +public class BitmexOrder extends BitmexMarketDataEvent { + + public enum OrderStatus { + NEW, + PARTIALLYFILLED, + FILLED, + TBD, + CANCELED, + REJECTED, + UNKNOW + } + + private String orderID; + + private int account; + + private String side; + + private BigDecimal price; + + private BigDecimal avgPx; + + private String ordType; + + private OrderStatus ordStatus; + + private String clOrdID; + + private BigDecimal orderQty; + + private BigDecimal cumQty; + + public boolean isNotWorkingIndicator() { + return !workingIndicator; + } + + private boolean workingIndicator; + + @JsonCreator + public BitmexOrder(@JsonProperty("symbol") String symbol, + @JsonProperty("timestamp") String timestamp, + @JsonProperty("orderID") String orderID, + @JsonProperty("account") int account, + @JsonProperty("side") String side, + @JsonProperty("price") BigDecimal price, + @JsonProperty("avgPx") BigDecimal avgPx, + @JsonProperty("ordType") String ordType, + @JsonProperty("ordStatus") String ordStatus, + @JsonProperty("clOrdID") String clOrdID, + @JsonProperty("orderQty") BigDecimal orderQty, + @JsonProperty("cumQty") BigDecimal cumQty, + @JsonProperty("workingIndicator") boolean workingIndicator) { + super(symbol, timestamp); + this.orderID = orderID; + this.account = account; + this.side = side; + this.price = price; + this.avgPx = avgPx; + this.ordType = ordType; + try { + this.ordStatus = OrderStatus.valueOf(ordStatus.toUpperCase()); + } catch (Exception e) { + this.ordStatus = OrderStatus.UNKNOW; + } + this.clOrdID = clOrdID; + this.orderQty = orderQty; + this.cumQty = cumQty; + this.workingIndicator = workingIndicator; + } + + public Order toOrder() { + Order.Builder order; + if (ordType.equals("Market")) { + order = new MarketOrder.Builder(side.equals("Buy") ? Order.OrderType.BID : Order.OrderType.ASK, BitmexUtils.translateBitmexCurrencyPair(symbol)); + } else { + order = new LimitOrder.Builder(side.equals("Buy") ? Order.OrderType.BID : Order.OrderType.ASK, BitmexUtils.translateBitmexCurrencyPair(symbol)); + ((LimitOrder.Builder)order).limitPrice(price); + } + order.id(orderID) + .averagePrice(avgPx) + .originalAmount(orderQty) + .cumulativeAmount(cumQty); + + switch (ordStatus) { + case NEW: + order.orderStatus(Order.OrderStatus.NEW); + break; + case PARTIALLYFILLED: + order.orderStatus(Order.OrderStatus.PARTIALLY_FILLED); + break; + case FILLED: + order.orderStatus(Order.OrderStatus.FILLED); + break; + case TBD: + order.orderStatus(Order.OrderStatus.PENDING_CANCEL); + break; + case CANCELED: + order.orderStatus(Order.OrderStatus.CANCELED); + break; + case REJECTED: + order.orderStatus(Order.OrderStatus.REJECTED); + default: + order.orderStatus(Order.OrderStatus.UNKNOWN); + break; + } + if (ordType.equals("Market")) { + return ((MarketOrder.Builder) order).build(); + } else { + return ((LimitOrder.Builder) order).build(); + } + } +} diff --git a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexWebSocketTransaction.java b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexWebSocketTransaction.java index 210789609..d0a4b0a4c 100644 --- a/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexWebSocketTransaction.java +++ b/xchange-bitmex/src/main/java/info/bitrich/xchangestream/bitmex/dto/BitmexWebSocketTransaction.java @@ -68,6 +68,21 @@ public BitmexTrade[] toBitmexTrades() { return trades; } + public BitmexOrder[] toBitmexOrders() { + BitmexOrder[] orders = new BitmexOrder[this.data.size()]; + for(int i = 0; i < this.data.size(); ++i) { + JsonNode jsonOrder = this.data.get(i); + + try { + orders[i] = (BitmexOrder) this.mapper.readValue(jsonOrder.toString(), BitmexOrder.class); + } catch (IOException var5) { + var5.printStackTrace(); + } + } + + return orders; + } + public String getTable() { return table; } @@ -79,4 +94,6 @@ public String getAction() { public JsonNode getData() { return data; } + + } diff --git a/xchange-okcoin/pom.xml b/xchange-okcoin/pom.xml index b8d99f529..6afea7181 100644 --- a/xchange-okcoin/pom.xml +++ b/xchange-okcoin/pom.xml @@ -25,5 +25,15 @@ xchange-okcoin ${xchange.version} + + com.google.guava + guava + 16.0 + + + org.apache.httpcomponents + fluent-hc + 4.5.5 + \ No newline at end of file diff --git a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinAuthenticator.java b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinAuthenticator.java new file mode 100644 index 000000000..df9891413 --- /dev/null +++ b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinAuthenticator.java @@ -0,0 +1,83 @@ +package info.bitrich.xchangestream.okcoin; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Created by heath on 2018/2/27. + */ + +public class OkCoinAuthenticator { + + public static String buildMysignV1(Map sArray, + String secretKey) { + String mysign = ""; + try { + String prestr = createLinkString(sArray); + prestr = prestr + "&secret_key=" + secretKey; + System.out.println("prestr " + prestr); + mysign = getMD5String(prestr); + } catch (Exception e) { + e.printStackTrace(); + } + return mysign; + } + + public static String createLinkString(Map params) { + + List keys = new ArrayList(params.keySet()); + Collections.sort(keys); + String prestr = ""; + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + if (i == keys.size() - 1) { + prestr = prestr + key + "=" + value; + } else { + prestr = prestr + key + "=" + value + "&"; + } + } + return prestr; + } + + /** + * 生成32位大写MD5值 + */ + private static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + public static String getMD5String(String str) { + try { + if (str == null || str.trim().length() == 0) { + return ""; + } + byte[] bytes = str.getBytes(); + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + messageDigest.update(bytes); + bytes = messageDigest.digest(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + sb.append(HEX_DIGITS[(bytes[i] & 0xf0) >> 4] + "" + + HEX_DIGITS[bytes[i] & 0xf]); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return ""; + } + + public static String getParams(Map map) { + StringBuilder params = new StringBuilder("{"); + for (Entry param : map.entrySet()) { + params.append("'").append(param.getKey()).append("':'").append(param.getValue()).append("',"); + } + params.replace(params.length() - 1, params.length(), "}"); + return params.toString(); + } +} \ No newline at end of file diff --git a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingExchange.java b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingExchange.java index 3321f6ae0..46c5cec80 100644 --- a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingExchange.java +++ b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingExchange.java @@ -23,6 +23,7 @@ protected OkCoinStreamingExchange(OkCoinStreamingService streamingService) { @Override protected void initServices() { super.initServices(); + streamingService.setExchangeSpecification(this.getExchangeSpecification()); streamingMarketDataService = new OkCoinStreamingMarketDataService(streamingService); } diff --git a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingService.java b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingService.java index 1d072a558..583a5e6ef 100644 --- a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingService.java +++ b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingService.java @@ -1,23 +1,35 @@ package info.bitrich.xchangestream.okcoin; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Collections2; import info.bitrich.xchangestream.okcoin.dto.WebSocketMessage; import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService; import info.bitrich.xchangestream.service.netty.WebSocketClientHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import io.reactivex.Completable; import io.reactivex.CompletableSource; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import io.reactivex.Observable; import io.reactivex.disposables.Disposable; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; +import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.exceptions.ExchangeException; -import java.io.IOException; -import java.util.concurrent.TimeUnit; public class OkCoinStreamingService extends JsonNettyStreamingService { + protected ExchangeSpecification exchangeSpecification; + private Observable pingPongSrc = Observable.interval(15, 15, TimeUnit.SECONDS); private Disposable pingPongSubscription; @@ -26,10 +38,36 @@ public OkCoinStreamingService(String apiUrl) { super(apiUrl); } + public void setExchangeSpecification(ExchangeSpecification exchangeSpecification) { + this.exchangeSpecification = exchangeSpecification; + } + + private void login() throws IOException { + String apiKey = this.exchangeSpecification.getApiKey(); + String apiSecret = this.exchangeSpecification.getSecretKey(); + Map keyAndSecret = new HashMap<>(); + keyAndSecret.put("api_key", apiKey); + keyAndSecret.put("secret_key", apiSecret); + Collection formData = Collections2.transform(keyAndSecret.entrySet(), + e -> new BasicNameValuePair(e.getKey(), e.getValue())); + String data = URLEncodedUtils.format(formData, Charset.forName("UTF-8")); + String sign = OkCoinAuthenticator.getMD5String(data); + + Map params = new HashMap<>(); + params.put("api_key", apiKey); + params.put("sign", sign); + Map cmd = new HashMap<>(); + cmd.put("event", "login"); + cmd.put("parameters", params); + + ObjectMapper objectMapper = new ObjectMapper(); + this.sendMessage(objectMapper.writeValueAsString(cmd)); + } + @Override public Completable connect() { Completable conn = super.connect(); - return conn.andThen((CompletableSource)(completable) -> { + return conn.andThen((CompletableSource) (completable) -> { try { if (pingPongSubscription != null && !pingPongSubscription.isDisposed()) { pingPongSubscription.dispose(); @@ -37,6 +75,9 @@ public Completable connect() { pingPongSubscription = pingPongSrc.subscribe(o -> { this.sendMessage("{\"event\":\"ping\"}"); }); + if (this.exchangeSpecification.getApiKey() != null) { + login(); + } completable.onComplete(); } catch (Exception e) { completable.onError(e); @@ -67,7 +108,7 @@ public String getUnsubscribeMessage(String channelName) throws IOException { @Override protected void handleMessage(JsonNode message) { - if (message.get("event") != null && "pong".equals(message.get("event").asText()) ) { + if (message.get("event") != null && "pong".equals(message.get("event").asText())) { // ignore pong message return; } diff --git a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingTradeService.java b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingTradeService.java new file mode 100644 index 000000000..a55f1e394 --- /dev/null +++ b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/OkCoinStreamingTradeService.java @@ -0,0 +1,49 @@ +package info.bitrich.xchangestream.okcoin; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.core.StreamingTradeService; +import info.bitrich.xchangestream.okcoin.dto.OkCoinFuturesOrder; +import io.reactivex.Observable; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; +import org.knowm.xchange.okcoin.FuturesContract; + + +public class OkCoinStreamingTradeService implements StreamingTradeService { + + private final OkCoinStreamingService streamingService; + + protected ExchangeSpecification exchangeSpecification; + + private final ObjectMapper mapper = new ObjectMapper(); + + public OkCoinStreamingTradeService(OkCoinStreamingService streamingService, ExchangeSpecification exchangeSpecification) { + this.streamingService = streamingService; + this.exchangeSpecification = exchangeSpecification; + this.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + @Override + public Observable getOrders(CurrencyPair currencyPair, Object... args) { + if (args.length < 1 || !(args[0] instanceof FuturesContract)) { + throw new NotYetImplementedForExchangeException(); + } + FuturesContract contract = (FuturesContract)args[0]; + String channel = "ok_sub_futureusd_trades"; + String instrument = String.format("%s_%s", currencyPair.counter.toString().toLowerCase(), currencyPair.base.toString().toLowerCase()); + + return this.streamingService.subscribeChannel(channel, new Object[0]) + .map(s -> this.mapper.treeToValue(s.get("data"), OkCoinFuturesOrder.class)) + .filter(order -> order.getSymbol().equals(instrument) && order.getContractType().equals(contract.getName())) + .map(OkCoinFuturesOrder::toOrder); + } + + @Override + public void submitOrder(Order order, CurrencyPair currencyPair, Object... args) { + throw new NotYetImplementedForExchangeException(); + } + +} \ No newline at end of file diff --git a/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/dto/OkCoinFuturesOrder.java b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/dto/OkCoinFuturesOrder.java new file mode 100644 index 000000000..683cf7563 --- /dev/null +++ b/xchange-okcoin/src/main/java/info/bitrich/xchangestream/okcoin/dto/OkCoinFuturesOrder.java @@ -0,0 +1,145 @@ +package info.bitrich.xchangestream.okcoin.dto; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.trade.LimitOrder; + +import java.math.BigDecimal; +import java.util.Date; + +public class OkCoinFuturesOrder { + + public enum OrderStatus { + PENDING, + PARTIAL_FILLED, + FULL_FILLED, + CANCELLED, + CANCELLING; + + public static OrderStatus fromInt(int value) { + if (value == -1) { + return CANCELLED; + } else { + return OrderStatus.values()[value]; + } + } + } + + public enum OrderType { + OPEN_LONG, + OPEN_SHORT, + SETTLE_LONG, + SETTLE_SHORT + } + + private BigDecimal contractId; + + private String contractName; + + private String contractType; + + private Date createDate; + + private BigDecimal amount; + + private BigDecimal dealAmount; + + private BigDecimal fee; + + private String orderId; + + private BigDecimal price; + + private BigDecimal priceAvg; + + private OrderStatus status; + + private OrderType type; + + private String symbol; + + @JsonCreator + public OkCoinFuturesOrder(@JsonProperty("contract_id") BigDecimal contractId, + @JsonProperty("contract_name") String contractName, + @JsonProperty("contract_type") String contractType, + @JsonProperty("create_date") long createDate, + @JsonProperty("amount") BigDecimal amount, + @JsonProperty("deal_amount") BigDecimal dealAmount, + @JsonProperty("fee") BigDecimal fee, + @JsonProperty("order_id") String orderId, + @JsonProperty("price") BigDecimal price, + @JsonProperty("price_avg") BigDecimal priceAvg, + @JsonProperty("status") int status, + @JsonProperty("type") int type, + @JsonProperty("symbol") String symbol) { + this.contractId = contractId; + this.contractName = contractName; + this.contractType = contractType; + this.createDate = new Date(createDate * 1000L); + this.amount = amount; + this.dealAmount = dealAmount; + this.fee = fee; + this.orderId = orderId; + this.price = price; + this.priceAvg = priceAvg; + this.status = OrderStatus.fromInt(status); + this.type = OrderType.values()[type-1]; + this.symbol = symbol; + } + + public String getSymbol() { + return symbol; + } + + public String getContractType() { + return contractType; + } + + public Order toOrder() { + Order.OrderType otype = null; + switch (type) { + case OPEN_LONG: + otype = Order.OrderType.BID; + break; + case OPEN_SHORT: + otype = Order.OrderType.ASK; + break; + case SETTLE_LONG: + otype = Order.OrderType.EXIT_BID; + break; + case SETTLE_SHORT: + otype = Order.OrderType.EXIT_ASK; + } + String [] currencies = symbol.split("_"); + CurrencyPair currencyPair = new CurrencyPair(new Currency(currencies[0].toUpperCase()), new Currency(currencies[1].toUpperCase())); + LimitOrder.Builder builder = new LimitOrder.Builder(otype, currencyPair); + builder.limitPrice(price); + builder.averagePrice(priceAvg); + builder.originalAmount(amount); + builder.cumulativeAmount(dealAmount); + switch (status) { + case PENDING: + builder.orderStatus(Order.OrderStatus.PENDING_NEW); + break; + case PARTIAL_FILLED: + builder.orderStatus(Order.OrderStatus.PARTIALLY_FILLED); + break; + case FULL_FILLED: + builder.orderStatus(Order.OrderStatus.FILLED); + break; + case CANCELLED: + builder.orderStatus(Order.OrderStatus.CANCELED); + break; + case CANCELLING: + builder.orderStatus(Order.OrderStatus.PENDING_CANCEL); + break; + default: + builder.orderStatus(Order.OrderStatus.UNKNOWN); + break; + } + return builder.build(); + } +} diff --git a/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingExchange.java b/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingExchange.java index b97a1332e..f801edbff 100644 --- a/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingExchange.java +++ b/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingExchange.java @@ -2,6 +2,7 @@ import io.reactivex.Completable; import org.knowm.xchange.Exchange; +import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; public interface StreamingExchange extends Exchange { /** @@ -31,6 +32,13 @@ public interface StreamingExchange extends Exchange { */ StreamingMarketDataService getStreamingMarketDataService(); + /** + * Returns service that can be used to access transaction data. + */ + default StreamingTradeService getStreamingTrasactionDataService() { + throw new NotYetImplementedForExchangeException(); + } + /** * Set whether or not to enable compression handler. * diff --git a/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingTradeService.java b/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingTradeService.java new file mode 100644 index 000000000..26ef91955 --- /dev/null +++ b/xchange-stream-core/src/main/java/info/bitrich/xchangestream/core/StreamingTradeService.java @@ -0,0 +1,19 @@ +package info.bitrich.xchangestream.core; + +import io.reactivex.Observable; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; + + + +public interface StreamingTradeService { + + default Observable getOrders(CurrencyPair var1, Object... var2) { + throw new NotYetImplementedForExchangeException(); + } + + default void submitOrder(Order order, CurrencyPair var1, Object...var2) { + throw new NotYetImplementedForExchangeException(); + } +}