Skip to content

Commit

Permalink
Merge pull request #1301 from ww3456/develop
Browse files Browse the repository at this point in the history
[Poloniex] order entry, amend and cancel improvements
  • Loading branch information
timmolter committed Aug 22, 2016
2 parents 3a25179 + 3b0be7a commit ea658c7
Show file tree
Hide file tree
Showing 15 changed files with 372 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.knowm.xchange.poloniex.dto.account.PoloniexBalance;
import org.knowm.xchange.poloniex.dto.account.PoloniexLoan;
import org.knowm.xchange.poloniex.dto.account.WithdrawalResponse;
import org.knowm.xchange.poloniex.dto.trade.PoloniexMoveResponse;
import org.knowm.xchange.poloniex.dto.trade.PoloniexOpenOrder;
import org.knowm.xchange.poloniex.dto.trade.PoloniexTradeResponse;
import org.knowm.xchange.poloniex.dto.trade.PoloniexUserTrade;
Expand Down Expand Up @@ -60,19 +61,26 @@ HashMap<String, PoloniexUserTrade[]> returnTradeHistory(@HeaderParam("Key") Stri
@FormParam("command")
PoloniexTradeResponse buy(@HeaderParam("Key") String apiKey, @HeaderParam("Sign") ParamsDigest signature,
@FormParam("nonce") SynchronizedValueFactory<Long> nonce, @FormParam("amount") String amount, @FormParam("rate") String rate,
@FormParam("currencyPair") String currencyPair) throws PoloniexException, IOException;
@FormParam("currencyPair") String currencyPair, @FormParam("fillOrKill") Integer fillOrKill,
@FormParam("immediateOrCancel") Integer immediateOrCancel, @FormParam("postOnly") Integer postOnly) throws PoloniexException, IOException;

@POST
@FormParam("command")
PoloniexTradeResponse sell(@HeaderParam("Key") String apiKey, @HeaderParam("Sign") ParamsDigest signature,
@FormParam("nonce") SynchronizedValueFactory<Long> nonce, @FormParam("amount") String amount, @FormParam("rate") String rate,
@FormParam("currencyPair") String currencyPair) throws PoloniexException, IOException;
@FormParam("currencyPair") String currencyPair, @FormParam("fillOrKill") Integer fillOrKill,
@FormParam("immediateOrCancel") Integer immediateOrCancel, @FormParam("postOnly") Integer postOnly) throws PoloniexException, IOException;

@POST
@FormParam("command")
PoloniexMoveResponse moveOrder(@HeaderParam("Key") String apiKey, @HeaderParam("Sign") ParamsDigest signature,
@FormParam("nonce") SynchronizedValueFactory<Long> nonce, @FormParam("orderNumber") String orderNumber, @FormParam("amount") String amount,
@FormParam("rate") String rate) throws PoloniexException, IOException;

@POST
@FormParam("command")
HashMap<String, String> cancelOrder(@HeaderParam("Key") String apiKey, @HeaderParam("Sign") ParamsDigest signature,
@FormParam("nonce") SynchronizedValueFactory<Long> nonce, @FormParam("orderNumber") String orderNumber,
@FormParam("currencyPair") String currencyPair) throws PoloniexException, IOException;
@FormParam("nonce") SynchronizedValueFactory<Long> nonce, @FormParam("orderNumber") String orderNumber) throws PoloniexException, IOException;

@POST
@FormParam("command")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.knowm.xchange.poloniex.dto.trade;

import java.math.BigDecimal;
import java.util.Date;

import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.trade.LimitOrder;

/**
* Poloniex order response contains details of any trades that have just executed in the order entry return value. If a LimitOrder of this type is
* supplied to the trade service orderEntry method it will be populated with this information.
*/
public class PoloniexLimitOrder extends LimitOrder {

private PoloniexTradeResponse response = null;

public PoloniexLimitOrder(OrderType type, BigDecimal tradableAmount, CurrencyPair currencyPair, String id, Date timestamp, BigDecimal limitPrice) {
super(type, tradableAmount, currencyPair, id, timestamp, limitPrice);
}

public void setResponse(PoloniexTradeResponse value) {
response = value;
}

public PoloniexTradeResponse getResponse() {
return response;
}

public static class Builder extends LimitOrder.Builder {

public Builder(OrderType orderType, CurrencyPair currencyPair) {
super(orderType, currencyPair);
}

public PoloniexLimitOrder build() {
final PoloniexLimitOrder order = new PoloniexLimitOrder(orderType, tradableAmount, currencyPair, id, timestamp, limitPrice);
order.setOrderFlags(flags);
return order;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.knowm.xchange.poloniex.dto.trade;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import si.mazi.rescu.ExceptionalReturnContentException;

import java.util.List;
import java.util.Map;

import org.knowm.xchange.poloniex.dto.marketdata.PoloniexPublicTrade;

public class PoloniexMoveResponse {

private boolean success;

private Long orderNumber;

private Map<String, List<PoloniexPublicTrade>> resultingTrades;

@JsonCreator
public PoloniexMoveResponse(@JsonProperty("success") Boolean success, @JsonProperty("orderNumber") Long orderNumber,
@JsonProperty("resultingTrades") Map<String, List<PoloniexPublicTrade>> resultingTrades) {

if (orderNumber == null) {
throw new ExceptionalReturnContentException("No trade data in response");
}
this.success = success;
this.orderNumber = orderNumber;
this.resultingTrades = resultingTrades;
}

public boolean success() {

return success;
}

public Long getOrderNumber() {

return orderNumber;
}

public Map<String, List<PoloniexPublicTrade>> getPoloniexPublicTrades() {

return resultingTrades;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.knowm.xchange.poloniex.dto.trade;

import org.knowm.xchange.dto.Order.IOrderFlags;

public enum PoloniexOrderFlags implements IOrderFlags {

/**
* A fill-or-kill order will either fill in its entirety or be completely aborted.
*/
FILL_OR_KILL,

/**
* An immediate-or-cancel order can be partially or completely filled, but any portion of the order that cannot be filled immediately will be
* cancelled rather than left on the order book.
*/
IMMEDIATE_OR_CANCEL,

/**
* A post-only order will only be placed if no portion of it fills immediately; this guarantees you will never pay the taker fee on any part of the
* order that fills.
*/
POST_ONLY;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.knowm.xchange.poloniex.dto.trade;

import java.util.ArrayList;
import java.math.BigDecimal;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonCreator;
Expand All @@ -12,29 +12,34 @@
public class PoloniexTradeResponse {

private final Long orderNumber;
private final List<PoloniexPublicTrade> resultingTrades = new ArrayList<PoloniexPublicTrade>();
private final List<PoloniexPublicTrade> resultingTrades;

/**
* Returned on FOK and IOC orders to indicate how much has been executed.
*/
private final BigDecimal amountUnfilled;

@JsonCreator
public PoloniexTradeResponse(@JsonProperty("orderNumber") Long orderNumber,
@JsonProperty("resultingTrades") List<PoloniexPublicTrade> resultingTrades) {
@JsonProperty("resultingTrades") List<PoloniexPublicTrade> resultingTrades, @JsonProperty("amountUnfilled") BigDecimal amountUnfilled) {

if (orderNumber == null) {
throw new ExceptionalReturnContentException("No trade data in response");
}
this.orderNumber = orderNumber;
if (resultingTrades != null) {
this.resultingTrades.addAll(resultingTrades);
}
this.resultingTrades = resultingTrades;
this.amountUnfilled = amountUnfilled;
}

public Long getOrderNumber() {

return orderNumber;
}

public List<PoloniexPublicTrade> getPoloniexPublicTrades() {

return resultingTrades;
}

public BigDecimal getAmountUnfilled() {
return amountUnfilled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException;
import org.knowm.xchange.poloniex.PoloniexAdapters;
import org.knowm.xchange.poloniex.PoloniexUtils;
import org.knowm.xchange.poloniex.dto.trade.PoloniexLimitOrder;
import org.knowm.xchange.poloniex.dto.trade.PoloniexOpenOrder;
import org.knowm.xchange.poloniex.dto.trade.PoloniexTradeResponse;
import org.knowm.xchange.poloniex.dto.trade.PoloniexUserTrade;
import org.knowm.xchange.service.polling.trade.PollingTradeService;
import org.knowm.xchange.service.polling.trade.params.TradeHistoryParamCurrencyPair;
Expand All @@ -39,11 +41,6 @@

public class PoloniexTradeService extends PoloniexTradeServiceRaw implements PollingTradeService {

/**
* Constructor
*
* @param exchange
*/
public PoloniexTradeService(Exchange exchange) {

super(exchange);
Expand All @@ -65,11 +62,21 @@ public String placeMarketOrder(MarketOrder marketOrder) throws IOException {
@Override
public String placeLimitOrder(LimitOrder limitOrder) throws IOException {

PoloniexTradeResponse response;
if (limitOrder.getType() == OrderType.BID) {
return buy(limitOrder).getOrderNumber().toString();
response = buy(limitOrder);
} else {
return sell(limitOrder).getOrderNumber().toString();
response = sell(limitOrder);
}

// The return value contains details of any trades that have been immediately executed as a result
// of this order. Make these available to the application if it has provided a PoloniexLimitOrder.
if (limitOrder instanceof PoloniexLimitOrder) {
PoloniexLimitOrder raw = (PoloniexLimitOrder) limitOrder;
raw.setResponse(response);
}

return response.getOrderNumber().toString();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,27 @@
import org.knowm.xchange.Exchange;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.trade.LimitOrder;
import org.knowm.xchange.dto.trade.OpenOrders;
import org.knowm.xchange.exceptions.ExchangeException;
import org.knowm.xchange.poloniex.PoloniexAdapters;
import org.knowm.xchange.poloniex.PoloniexAuthenticated;
import org.knowm.xchange.poloniex.PoloniexException;
import org.knowm.xchange.poloniex.PoloniexUtils;
import org.knowm.xchange.poloniex.dto.trade.PoloniexMoveResponse;
import org.knowm.xchange.poloniex.dto.trade.PoloniexOpenOrder;
import org.knowm.xchange.poloniex.dto.trade.PoloniexOrderFlags;
import org.knowm.xchange.poloniex.dto.trade.PoloniexTradeResponse;
import org.knowm.xchange.poloniex.dto.trade.PoloniexUserTrade;

import si.mazi.rescu.ParamsDigest;
import si.mazi.rescu.SynchronizedValueFactory;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;

public class PoloniexTradeServiceRaw extends PoloniexBasePollingService {

/**
* Constructor
*
* @param exchange
*/
public PoloniexTradeServiceRaw(Exchange exchange) {

super(exchange);
Expand All @@ -49,62 +51,66 @@ public HashMap<String, PoloniexUserTrade[]> returnTradeHistory(Long startTime, L
}

public PoloniexTradeResponse buy(LimitOrder limitOrder) throws IOException {
return orderEntry(limitOrder, "buy");
}

public PoloniexTradeResponse sell(LimitOrder limitOrder) throws IOException {
return orderEntry(limitOrder, "sell");
}

private PoloniexTradeResponse orderEntry(LimitOrder limitOrder, String name) throws IOException {
Integer fillOrKill;
if (limitOrder.getOrderFlags().contains(PoloniexOrderFlags.FILL_OR_KILL)) {
fillOrKill = 1;
} else {
fillOrKill = null;
}

Integer immediateOrCancel;
if (limitOrder.getOrderFlags().contains(PoloniexOrderFlags.IMMEDIATE_OR_CANCEL)) {
immediateOrCancel = 1;
} else {
immediateOrCancel = null;
}

Integer postOnly;
if (limitOrder.getOrderFlags().contains(PoloniexOrderFlags.POST_ONLY)) {
postOnly = 1;
} else {
postOnly = null;
}

try {
PoloniexTradeResponse response = poloniexAuthenticated.buy(apiKey, signatureCreator, exchange.getNonceFactory(),
limitOrder.getTradableAmount().toPlainString(), limitOrder.getLimitPrice().toPlainString(),
PoloniexUtils.toPairString(limitOrder.getCurrencyPair()));
Method method = PoloniexAuthenticated.class.getDeclaredMethod(name, String.class, ParamsDigest.class, SynchronizedValueFactory.class,
String.class, String.class, String.class, Integer.class, Integer.class, Integer.class);
PoloniexTradeResponse response = (PoloniexTradeResponse) method.invoke(poloniexAuthenticated, apiKey, signatureCreator,
exchange.getNonceFactory(), limitOrder.getTradableAmount().toPlainString(), limitOrder.getLimitPrice().toPlainString(),
PoloniexUtils.toPairString(limitOrder.getCurrencyPair()), fillOrKill, immediateOrCancel, postOnly);
return response;
} catch (PoloniexException e) {
throw new ExchangeException(e.getError(), e);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new ExchangeException(e.getMessage(), e);
}
}

public PoloniexTradeResponse sell(LimitOrder limitOrder) throws IOException {
public PoloniexMoveResponse move(String orderId, BigDecimal tradableAmount, BigDecimal limitPrice) throws IOException {

try {
PoloniexTradeResponse response = poloniexAuthenticated.sell(apiKey, signatureCreator, exchange.getNonceFactory(),
limitOrder.getTradableAmount().toPlainString(), limitOrder.getLimitPrice().toPlainString(),
PoloniexUtils.toPairString(limitOrder.getCurrencyPair()));
return response;
return poloniexAuthenticated.moveOrder(apiKey, signatureCreator, exchange.getNonceFactory(), orderId, tradableAmount.toPlainString(),
limitPrice.toPlainString());
} catch (PoloniexException e) {
throw new ExchangeException(e.getError(), e);
}
}

public boolean cancel(String orderId) throws IOException {

/*
* Need to look up CurrencyPair associated with orderId Poloniex is working on fixing this
*/
OpenOrders openOrders = PoloniexAdapters.adaptPoloniexOpenOrders(returnOpenOrders());
for (LimitOrder order : openOrders.getOpenOrders()) {
if (order.getId().equals(orderId)) {
HashMap<String, String> response = poloniexAuthenticated.cancelOrder(apiKey, signatureCreator, exchange.getNonceFactory(), orderId,
PoloniexUtils.toPairString(order.getCurrencyPair()));
if (response.containsKey("error")) {
throw new ExchangeException(response.get("error"));
} else {
return response.get("success").toString().equals(new Integer(1).toString()) ? true : false;
}
}
}

throw new ExchangeException("Unable to find order #" + orderId);

}

public boolean cancel(String orderId, CurrencyPair currencyPair) throws IOException {

/*
* No need to look up CurrencyPair associated with orderId, as the caller will provide it.
*/
HashMap<String, String> response = poloniexAuthenticated.cancelOrder(apiKey, signatureCreator, exchange.getNonceFactory(), orderId,
PoloniexUtils.toPairString(currencyPair));
HashMap<String, String> response = poloniexAuthenticated.cancelOrder(apiKey, signatureCreator, exchange.getNonceFactory(), orderId);
if (response.containsKey("error")) {
throw new ExchangeException(response.get("error"));
} else {
return response.get("success").toString().equals(new Integer(1).toString()) ? true : false;
}
return response.get("success").toString().equals(new Integer(1).toString()) ? true : false;
}

public HashMap<String, String> getFeeInfo() throws IOException {
Expand Down
Loading

0 comments on commit ea658c7

Please sign in to comment.