Skip to content

Commit

Permalink
added ability to CancelOrder() by ClientOrderId (#734)
Browse files Browse the repository at this point in the history
- many, but not all, exchanges support this
  • Loading branch information
vslee authored Feb 11, 2022
1 parent ddfb36e commit ddc7138
Show file tree
Hide file tree
Showing 29 changed files with 98 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,9 @@ protected override async Task<ExchangeOrderResult> OnGetOrderDetailsAsync(string
return orderDetails;
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
var payload = await GetNoncePayloadAsync();
payload["orderId"] = orderId;
JToken token = await MakeJsonRequestAsync<JToken>("/trades/v1/order", null, payload, "DELETE");
Expand Down
3 changes: 2 additions & 1 deletion src/ExchangeSharp/API/Exchanges/BL3P/ExchangeBL3PAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,11 @@ protected override async Task<ExchangeOrderBook> OnGetOrderBookAsync(string mark
return ConvertToExchangeOrderBook(maxCount, bl3pOrderBook);
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (string.IsNullOrWhiteSpace(marketSymbol))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(marketSymbol));
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");

var resultBody = await MakeRequestAsync(
$"/{marketSymbol}/money/order/cancel",
Expand Down
2 changes: 1 addition & 1 deletion src/ExchangeSharp/API/Exchanges/BTSE/ExchangeBTSEAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(strin
this.ParseCandle(token, marketSymbol, periodSeconds, 1, 2, 3, 4, 0, TimestampType.UnixMilliseconds, 5));
}

protected override async Task OnCancelOrderAsync(string orderId, string? marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string? marketSymbol = null, bool isClientOrderId = false)
{
var payload = await GetNoncePayloadAsync();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,15 +733,18 @@ private async Task<IEnumerable<ExchangeOrderResult>> OnGetMyTradesAsync(string?
return trades;
}

protected override async Task OnCancelOrderAsync(string orderId, string? marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string? marketSymbol = null, bool isClientOrderId = false)
{
Dictionary<string, object> payload = await GetNoncePayloadAsync();
if (string.IsNullOrWhiteSpace(marketSymbol))
{
throw new ArgumentNullException("Binance cancel order request requires symbol");
}
payload["symbol"] = marketSymbol!;
payload["orderId"] = orderId;
if (isClientOrderId) // Either orderId or origClientOrderId must be sent.
payload["origClientOrderId"] = orderId;
else
payload["orderId"] = orderId;
var token = await MakeJsonRequestAsync<JToken>("/order", BaseUrlApi, payload, "DELETE");
var cancelledOrder = ParseOrder(token);
if (cancelledOrder.OrderId != orderId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
return ParseOrder(token);
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
Dictionary<string, object> payload = await GetNoncePayloadAsync();
if (marketSymbol == null)
throw new APIException("Bitbank requries market symbol when cancelling orders");
Expand Down
4 changes: 2 additions & 2 deletions src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,10 @@ protected override async Task<ExchangeOrderResult> OnGetOrderDetailsAsync(string
return orders[0];
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
Dictionary<string, object> payload = await GetNoncePayloadAsync();
payload["orderID"] = orderId;
payload[isClientOrderId ? "clOrdID" : "orderID"] = orderId;
JToken token = await MakeJsonRequestAsync<JToken>("/order", BaseUrl, payload, "DELETE");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,8 +569,9 @@ protected override Task<IWebSocket> OnGetCompletedOrderDetailsWebSocketAsync(Act
});
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
Dictionary<string, object> payload = await GetNoncePayloadAsync();
payload["order_id"] = orderId.ConvertInvariant<long>();
var token = await MakeJsonRequestAsync<JToken>("/order/cancel", BaseUrlV1, payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,10 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetCompletedOr
return orders2;
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
{
if (string.IsNullOrWhiteSpace(orderId))
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
if (string.IsNullOrWhiteSpace(orderId))
{
throw new APIException("OrderId is needed for canceling order");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,9 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetCompletedOr
return orders;
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
await MakeJsonRequestAsync<JToken>("/orders/" + orderId, null, await GetNoncePayloadAsync(), "DELETE");
}

Expand Down
7 changes: 5 additions & 2 deletions src/ExchangeSharp/API/Exchanges/Bybit/ExchangeBybitAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -778,10 +778,13 @@ protected override async Task<ExchangeOrderResult> OnGetOrderDetailsAsync(string
}
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
var extraParams = new Dictionary<string, object>();
extraParams["order_id"] = orderId;
if (isClientOrderId)
extraParams["order_link_id"] = orderId;
else
extraParams["order_id"] = orderId;
if (!string.IsNullOrWhiteSpace(marketSymbol))
{
extraParams["symbol"] = marketSymbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -807,9 +807,9 @@ private async Task MakeFillRequest(DateTime? afterDate, string productId, List<E
}
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
var jToken = await MakeJsonRequestAsync<JToken>("orders/" + orderId, null, await GetNoncePayloadAsync(), "DELETE");
var jToken = await MakeJsonRequestAsync<JToken>("orders/" + (isClientOrderId ? "client:" : "") + orderId, null, await GetNoncePayloadAsync(), "DELETE");
if (jToken.ToStringInvariant() != orderId)
throw new APIException($"Cancelled {jToken.ToStringInvariant()} when trying to cancel {orderId}");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ protected override async Task<ExchangeOrderResult> OnGetOrderDetailsAsync(string
};
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
var payload = await GetNoncePayloadAsync();
payload["orderId"] = orderId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,9 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
return new ExchangeOrderResult { OrderId = token["order_id"].ToStringInvariant() };
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
Dictionary<string, object> payload = await GetNoncePayloadAsync();
payload["order_id"] = orderId;
JToken token = await MakeJsonRequestAsync<JToken>("/spot/order/cancel", payload: payload, requestMethod: "POST");
Expand Down
10 changes: 8 additions & 2 deletions src/ExchangeSharp/API/Exchanges/FTX/FTXGroupCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ public FTXGroupCommon()
#region [ Implementation ]

/// <inheritdoc />
protected async override Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected async override Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
await MakeJsonRequestAsync<JToken>($"/orders/{orderId}", null, await GetNoncePayloadAsync(), "DELETE");
var url = "/orders/";
if (isClientOrderId)
{
url += "by_client_id/";
}

await MakeJsonRequestAsync<JToken>($"{url}{orderId}", null, await GetNoncePayloadAsync(), "DELETE");
}

/// <inheritdoc />
Expand Down
3 changes: 2 additions & 1 deletion src/ExchangeSharp/API/Exchanges/GateIo/ExchangeGateIoAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,13 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetCompletedOr
return responseToken.Select(x => ParseOrder(x)).ToArray();
}

protected override async Task OnCancelOrderAsync(string orderId, string symbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string symbol = null, bool isClientOrderId = false)
{
if (string.IsNullOrEmpty(symbol))
{
throw new InvalidOperationException("MarketSymbol is required for cancelling order with Gate.io API");
}
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");

Dictionary<string, object> payload = await GetNoncePayloadAsync();
await MakeJsonRequestAsync<JToken>($"/spot/orders/{orderId}?currency_pair={symbol}", BaseUrl, payload, "DELETE");
Expand Down
3 changes: 2 additions & 1 deletion src/ExchangeSharp/API/Exchanges/Gemini/ExchangeGeminiAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,9 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetOpenOrderDe
return orders;
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
object nonce = await GenerateNonceAsync();
await MakeJsonRequestAsync<JToken>("/order/cancel", null, new Dictionary<string, object> { { "nonce", nonce }, { "order_id", orderId } });
}
Expand Down
9 changes: 5 additions & 4 deletions src/ExchangeSharp/API/Exchanges/Hitbtc/ExchangeHitbtcAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,10 +353,11 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
return result;
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
{
// this call returns info about the success of the cancel. Sure would be nice have a return type on this method.
JToken token = await MakeJsonRequestAsync<JToken>("/order/" + orderId, null, await GetNoncePayloadAsync(), "DELETE");
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
// this call returns info about the success of the cancel. Sure would be nice have a return type on this method.
JToken token = await MakeJsonRequestAsync<JToken>("/order/" + orderId, null, await GetNoncePayloadAsync(), "DELETE");
}

private void ParseAveragePriceAndFeesFromFills(ExchangeOrderResult result, JToken fillsToken)
Expand Down
12 changes: 9 additions & 3 deletions src/ExchangeSharp/API/Exchanges/Huobi/ExchangeHuobiAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -717,10 +717,16 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
return ParsePlaceOrder(obj, order);
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
{
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
var payload = await GetNoncePayloadAsync();
await MakeJsonRequestAsync<JToken>($"/order/orders/{orderId}/submitcancel", PrivateUrlV1, payload, "POST");
JToken data;
if (isClientOrderId)
{
payload.Add("clientOrderId", orderId);
data = await MakeJsonRequestAsync<JToken>($"/order/orders/submitCancelClientOrder", PrivateUrlV1, payload, "POST");
}
else data = await MakeJsonRequestAsync<JToken>($"/order/orders/{orderId}/submitcancel", PrivateUrlV1, payload, "POST");
}

protected override async Task<IEnumerable<ExchangeTransaction>> OnGetDepositHistoryAsync(string currency)
Expand Down
3 changes: 2 additions & 1 deletion src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -768,8 +768,9 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetCompletedOr
// return token["trades"].Select(t => TradeHistoryToExchangeOrderResult(t));
//}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
object nonce = await GenerateNonceAsync();
Dictionary<string, object> payload = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
{ { "txid", orderId }, { "nonce", nonce }
Expand Down
15 changes: 3 additions & 12 deletions src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,20 +422,11 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
/// </summary>
/// <param name="orderId">The Original Order Id return from Place Order</param>
/// <returns></returns>
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
{
// Find order detail
ExchangeOrderResult order = await GetOrderDetailsAsync(orderId, marketSymbol: marketSymbol);

// There is no order to be cancelled
if (order == null)
{
return;
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
var payload = await GetNoncePayloadAsync();

JToken token = await MakeJsonRequestAsync<JToken>("/orders/" + orderId, null, payload, "DELETE");
JToken token = await MakeJsonRequestAsync<JToken>(isClientOrderId ? "/order/client-order/" : "/orders/" + orderId, null, payload, "DELETE");
}

protected override async Task<ExchangeDepositDetails> OnGetDepositAddressAsync(string currency, bool forceRegenerate = false)
Expand Down
3 changes: 2 additions & 1 deletion src/ExchangeSharp/API/Exchanges/LBank/ExchangeLBankAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,9 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetCompletedOr
}

//CancelOrder 12
protected override async Task OnCancelOrderAsync(string orderId, string symbol = null)
protected override async Task OnCancelOrderAsync(string orderId, string symbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
Dictionary<string, object> payload = new Dictionary<string, object>
{
{ "api_key", PublicApiKey.ToUnsecureString() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,11 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
return new ExchangeOrderResult() { OrderId = token["orderId"].ToStringInvariant(), Result = ExchangeAPIOrderResult.Open };
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null)
{
// can only cancel limit orders, which kinda makes sense, but we also need the currency pair, which requires a lookup
var order = await OnGetOrderDetailsAsync(orderId);
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
// can only cancel limit orders, which kinda makes sense, but we also need the currency pair, which requires a lookup
var order = await OnGetOrderDetailsAsync(orderId);
if (order != null)
{
// { "success": true,"cancelled": true,"message": null,"quantity": 0.0005,"tradeQuantity": 0}
Expand Down
7 changes: 4 additions & 3 deletions src/ExchangeSharp/API/Exchanges/NDAX/ExchangeNDAXAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ protected override async Task<IEnumerable<ExchangeOrderResult>> OnGetOpenOrderDe
}


protected override async Task OnCancelOrderAsync(string orderId, string symbol = null)
{
var result = await MakeJsonRequestAsync<GenericResponse>("CancelOrder", null,
protected override async Task OnCancelOrderAsync(string orderId, string symbol = null, bool isClientOrderId = false)
{
if (isClientOrderId) throw new NotSupportedException("Cancelling by client order ID is not supported in ExchangeSharp. Please submit a PR if you are interested in this feature");
var result = await MakeJsonRequestAsync<GenericResponse>("CancelOrder", null,
new Dictionary<string, object>()
{
{"OrderId", orderId},
Expand Down
7 changes: 5 additions & 2 deletions src/ExchangeSharp/API/Exchanges/OKGroup/ExchangeOKExAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ protected override async Task<ExchangeOrderResult> OnGetOrderDetailsAsync(string
return ParseOrders(token).First();
}

protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol)
protected override async Task OnCancelOrderAsync(string orderId, string marketSymbol, bool isClientOrderId = false)
{
if (string.IsNullOrEmpty(orderId))
{
Expand All @@ -308,7 +308,10 @@ protected override async Task OnCancelOrderAsync(string orderId, string marketSy
}

var payload = await GetNoncePayloadAsync();
payload["ordId"] = orderId;
if (isClientOrderId)
payload["client_oid "] = orderId;
else
payload["ordId"] = orderId;
payload["instId"] = marketSymbol;
await MakeJsonRequestAsync<JToken>("/trade/cancel-order", BaseUrlV5, payload, "POST");
}
Expand Down
Loading

0 comments on commit ddc7138

Please sign in to comment.