From fe57b9b20f9086774e795efc0e86eed0097b428d Mon Sep 17 00:00:00 2001 From: Victor Lee Date: Sat, 18 Sep 2021 19:11:37 -0700 Subject: [PATCH] Standardize isPostOnly - a.k.a. MakerOnly or BookOrCancel or PendingOrCancelled - most exchanges support this, but vary in the parameters needed to specify this --- .../Exchanges/Aquanow/ExchangeAquanowAPI.cs | 1 + .../API/Exchanges/BL3P/ExchangeBL3PAPI.cs | 1 + .../API/Exchanges/BTSE/ExchangeBTSEAPI.cs | 1 + .../BinanceGroup/BinanceGroupCommon.cs | 5 +++++ .../Exchanges/BitBank/ExchangeBitBankAPI.cs | 2 ++ .../API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs | 8 ++++---- .../Exchanges/Bitfinex/ExchangeBitfinexAPI.cs | 1 + .../Exchanges/Bitstamp/ExchangeBitstampAPI.cs | 3 ++- .../Exchanges/Bittrex/ExchangeBittrexAPI.cs | 3 ++- .../API/Exchanges/Bybit/ExchangeBybitAPI.cs | 1 + .../Exchanges/Coinbase/ExchangeCoinbaseAPI.cs | 4 +--- .../Digifinex/ExchangeDigifinexAPI.cs | 1 + .../API/Exchanges/GateIo/ExchangeGateIoAPI.cs | 1 + .../API/Exchanges/Gemini/ExchangeGeminiAPI.cs | 1 + .../API/Exchanges/Hitbtc/ExchangeHitbtcAPI.cs | 3 ++- .../API/Exchanges/Huobi/ExchangeHuobiAPI.cs | 4 +++- .../API/Exchanges/Kraken/ExchangeKrakenAPI.cs | 1 + .../API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs | 1 + .../API/Exchanges/LBank/ExchangeLBankAPI.cs | 1 + .../Exchanges/Livecoin/ExchangeLivecoinAPI.cs | 3 ++- .../API/Exchanges/NDAX/ExchangeNDAXAPI.cs | 3 ++- .../API/Exchanges/OKGroup/OKGroupCommon.cs | 5 +++-- .../Exchanges/Poloniex/ExchangePoloniexAPI.cs | 3 ++- .../API/Exchanges/Yobit/ExchangeYobitAPI.cs | 3 ++- .../API/Exchanges/_Base/ExchangeAPI.cs | 1 + .../API/Exchanges/_Base/IExchangeAPI.cs | 6 +++--- .../Model/ExchangeOrderRequest.cs | 20 ++++++++++++------- 27 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/ExchangeSharp/API/Exchanges/Aquanow/ExchangeAquanowAPI.cs b/src/ExchangeSharp/API/Exchanges/Aquanow/ExchangeAquanowAPI.cs index 517b7097..8eed8c4c 100644 --- a/src/ExchangeSharp/API/Exchanges/Aquanow/ExchangeAquanowAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Aquanow/ExchangeAquanowAPI.cs @@ -105,6 +105,7 @@ protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dicti // DONE protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); // In Aquanow market order, when buying crypto the amount of crypto that is bought is the receiveQuantity // and when selling the amount of crypto that is sold is the deliverQuantity string amountParameter = order.IsBuy ? "receiveQuantity" : "deliverQuantity"; diff --git a/src/ExchangeSharp/API/Exchanges/BL3P/ExchangeBL3PAPI.cs b/src/ExchangeSharp/API/Exchanges/BL3P/ExchangeBL3PAPI.cs index ddef36e2..474227d0 100644 --- a/src/ExchangeSharp/API/Exchanges/BL3P/ExchangeBL3PAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/BL3P/ExchangeBL3PAPI.cs @@ -233,6 +233,7 @@ private string GetSignKey(IHttpWebRequest request, string formData) protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); var roundedAmount = order.RoundAmount(); var amountInt = converterToEight.FromDecimal(roundedAmount); diff --git a/src/ExchangeSharp/API/Exchanges/BTSE/ExchangeBTSEAPI.cs b/src/ExchangeSharp/API/Exchanges/BTSE/ExchangeBTSEAPI.cs index bf5f826c..1d6c1611 100644 --- a/src/ExchangeSharp/API/Exchanges/BTSE/ExchangeBTSEAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/BTSE/ExchangeBTSEAPI.cs @@ -165,6 +165,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd dict.Add("size", request.Amount); dict.Add("side", request.IsBuy ? "BUY" : "SELL"); dict.Add("symbol", request.MarketSymbol); + if (request.IsPostOnly != null) payload["postOnly"] = request.IsPostOnly; switch (request.OrderType ) { diff --git a/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs b/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs index 21024b50..392a0019 100644 --- a/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs +++ b/src/ExchangeSharp/API/Exchanges/BinanceGroup/BinanceGroupCommon.cs @@ -554,6 +554,11 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd payload["side"] = order.IsBuy ? "BUY" : "SELL"; if (order.OrderType == OrderType.Stop) payload["type"] = "STOP_LOSS";//if order type is stop loss/limit, then binance expect word 'STOP_LOSS' inestead of 'STOP' + else if (order.IsPostOnly == true) + { + if (order.OrderType == OrderType.Limit) payload["type"] = "LIMIT_MAKER"; // LIMIT_MAKER are LIMIT orders that will be rejected if they would immediately match and trade as a taker. + else throw new NotImplementedException("PostOnly with non limit orders are not currently supported on Binance. Please submit a PR if you are interested in this feature"); + } else payload["type"] = order.OrderType.ToStringUpperInvariant(); diff --git a/src/ExchangeSharp/API/Exchanges/BitBank/ExchangeBitBankAPI.cs b/src/ExchangeSharp/API/Exchanges/BitBank/ExchangeBitBankAPI.cs index e6b15bb9..492a3c73 100644 --- a/src/ExchangeSharp/API/Exchanges/BitBank/ExchangeBitBankAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/BitBank/ExchangeBitBankAPI.cs @@ -129,6 +129,8 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd if (order.OrderType == OrderType.Stop) throw new InvalidOperationException("Bitbank does not support stop order"); Dictionary payload = await GetNoncePayloadAsync(); + if (order.IsPostOnly != null) + payload["post_only"] = order.IsPostOnly; payload.Add("pair", NormalizeMarketSymbol(order.MarketSymbol)); payload.Add("amount", order.Amount.ToStringInvariant()); payload.Add("side", order.IsBuy ? "buy" : "sell"); diff --git a/src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs b/src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs index 17e473d5..33e212d4 100644 --- a/src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs @@ -693,10 +693,10 @@ private void AddOrderToPayload(ExchangeOrderRequest order, Dictionary OnPlaceOrderAsync(ExchangeOrd { payload["price"] = "1"; } + if (order.IsPostOnly == true) payload["flags"] = "4096"; // The post-only limit order option ensures the limit order will be added to the order book and not match with a pre-existing order unless the pre-existing order is a hidden order. order.ExtraParameters.CopyTo(payload); JToken obj = await MakeJsonRequestAsync("/order/new", BaseUrlV1, payload); return ParseOrder(obj); diff --git a/src/ExchangeSharp/API/Exchanges/Bitstamp/ExchangeBitstampAPI.cs b/src/ExchangeSharp/API/Exchanges/Bitstamp/ExchangeBitstampAPI.cs index f27008aa..e1fc23b5 100644 --- a/src/ExchangeSharp/API/Exchanges/Bitstamp/ExchangeBitstampAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Bitstamp/ExchangeBitstampAPI.cs @@ -195,7 +195,8 @@ private static Dictionary ExtractDictionary(JObject responseObj protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { - string action = order.IsBuy ? "buy" : "sell"; + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); + string action = order.IsBuy ? "buy" : "sell"; string market = order.OrderType == OrderType.Market ? "/market" : ""; string url = $"/{action}{market}/{order.MarketSymbol}/"; Dictionary payload = await GetNoncePayloadAsync(); diff --git a/src/ExchangeSharp/API/Exchanges/Bittrex/ExchangeBittrexAPI.cs b/src/ExchangeSharp/API/Exchanges/Bittrex/ExchangeBittrexAPI.cs index 781c5a6d..85b7b655 100644 --- a/src/ExchangeSharp/API/Exchanges/Bittrex/ExchangeBittrexAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Bittrex/ExchangeBittrexAPI.cs @@ -389,7 +389,8 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd if (order.OrderType == ExchangeSharp.OrderType.Limit) { orderParams.Add("limit", orderPrice); - orderParams.Add("timeInForce", "GOOD_TIL_CANCELLED"); + if (order.IsPostOnly == true) orderParams.Add("timeInForce", "POST_ONLY_GOOD_TIL_CANCELLED"); // This option allows market makers to ensure that their orders are making it to the order book instead of matching with a pre-existing order. Note: If the order is not a maker order, you will return an error and the order will be cancelled + else orderParams.Add("timeInForce", "GOOD_TIL_CANCELLED"); } foreach (KeyValuePair kv in order.ExtraParameters) diff --git a/src/ExchangeSharp/API/Exchanges/Bybit/ExchangeBybitAPI.cs b/src/ExchangeSharp/API/Exchanges/Bybit/ExchangeBybitAPI.cs index 0d93d122..f89dd61d 100644 --- a/src/ExchangeSharp/API/Exchanges/Bybit/ExchangeBybitAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Bybit/ExchangeBybitAPI.cs @@ -816,6 +816,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd public async Task OnAmendOrderAsync(ExchangeOrderRequest order) { + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); var payload = new Dictionary(); payload["symbol"] = order.MarketSymbol; if(order.OrderId != null) diff --git a/src/ExchangeSharp/API/Exchanges/Coinbase/ExchangeCoinbaseAPI.cs b/src/ExchangeSharp/API/Exchanges/Coinbase/ExchangeCoinbaseAPI.cs index d0ce1a9c..bfc789e3 100644 --- a/src/ExchangeSharp/API/Exchanges/Coinbase/ExchangeCoinbaseAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Coinbase/ExchangeCoinbaseAPI.cs @@ -570,9 +570,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd switch (order.OrderType) { case OrderType.Limit: - // set payload["post_only"] to true for default scenario when order.ExtraParameters["post_only"] is not specified - // to place non-post-only limit order one can set and pass order.ExtraParameters["post_only"]="false" - payload["post_only"] = order.ExtraParameters.TryGetValueOrDefault("post_only", "true"); + if (order.IsPostOnly != null) payload["post_only"] = order.IsPostOnly; // [optional]** Post only flag, ** Invalid when time_in_force is IOC or FOK if (order.Price == null) throw new ArgumentNullException(nameof(order.Price)); payload["price"] = order.Price.ToStringInvariant(); break; diff --git a/src/ExchangeSharp/API/Exchanges/Digifinex/ExchangeDigifinexAPI.cs b/src/ExchangeSharp/API/Exchanges/Digifinex/ExchangeDigifinexAPI.cs index 6efa71ec..cbf36683 100644 --- a/src/ExchangeSharp/API/Exchanges/Digifinex/ExchangeDigifinexAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Digifinex/ExchangeDigifinexAPI.cs @@ -423,6 +423,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd payload["type"] = GetOrderType(order); payload["price"] = order.Price; payload["amount"] = order.Amount; + if (order.IsPostOnly != null) payload["post_only"] = order.IsPostOnly.Value ? "1" : "0"; // Default 0, enabled by 1, if enabled the order will be cancelled if it can be executed immediately, making sure there will be no market taking var market = order.IsMargin ? "margin" : "spot"; JToken token = await MakeJsonRequestAsync($"/{market}/order/new", payload: payload, requestMethod: "POST"); return new ExchangeOrderResult { OrderId = token["order_id"].ToStringInvariant() }; diff --git a/src/ExchangeSharp/API/Exchanges/GateIo/ExchangeGateIoAPI.cs b/src/ExchangeSharp/API/Exchanges/GateIo/ExchangeGateIoAPI.cs index c1c7c4c3..5783e977 100644 --- a/src/ExchangeSharp/API/Exchanges/GateIo/ExchangeGateIoAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/GateIo/ExchangeGateIoAPI.cs @@ -335,6 +335,7 @@ private void AddOrderToPayload(ExchangeOrderRequest order, Dictionary OnPlaceOrderAsync(ExchangeOrd { "side", (order.IsBuy ? "buy" : "sell") }, { "type", "exchange limit" } }; + if (order.IsPostOnly == true) payload["options"] = "[maker-or-cancel]"; // This order will only add liquidity to the order book. If any part of the order could be filled immediately, the whole order will instead be canceled before any execution occurs. If that happens, the response back from the API will indicate that the order has already been canceled("is_cancelled": true in JSON). Note: some other exchanges call this option "post-only". order.ExtraParameters.CopyTo(payload); JToken obj = await MakeJsonRequestAsync("/order/new", null, payload); return ParseOrder(obj); diff --git a/src/ExchangeSharp/API/Exchanges/Hitbtc/ExchangeHitbtcAPI.cs b/src/ExchangeSharp/API/Exchanges/Hitbtc/ExchangeHitbtcAPI.cs index 2887ddc6..e015032c 100644 --- a/src/ExchangeSharp/API/Exchanges/Hitbtc/ExchangeHitbtcAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Hitbtc/ExchangeHitbtcAPI.cs @@ -320,7 +320,8 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd payload["price"] = order.Price; payload["timeInForce"] = "GTC"; } - order.ExtraParameters.CopyTo(payload); + if (order.IsPostOnly != null) payload["post_only"] = order.IsPostOnly; // Optional. If your post-only order causes a match with a pre-existing order as a taker, then the order will be cancelled. + order.ExtraParameters.CopyTo(payload); // { "id": 0,"clientOrderId": "d8574207d9e3b16a4a5511753eeef175","symbol": "ETHBTC","side": "sell","status": "new","type": "limit","timeInForce": "GTC","quantity": "0.063","price": "0.046016","cumQuantity": "0.000","createdAt": "2017-05-15T17:01:05.092Z","updatedAt": "2017-05-15T17:01:05.092Z" } JToken token = await MakeJsonRequestAsync("/order", null, payload, "POST"); diff --git a/src/ExchangeSharp/API/Exchanges/Huobi/ExchangeHuobiAPI.cs b/src/ExchangeSharp/API/Exchanges/Huobi/ExchangeHuobiAPI.cs index 74a10dc1..4403f3ae 100644 --- a/src/ExchangeSharp/API/Exchanges/Huobi/ExchangeHuobiAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Huobi/ExchangeHuobiAPI.cs @@ -705,7 +705,9 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd payload["price"] = outputPrice.ToStringInvariant(); } - order.ExtraParameters.CopyTo(payload); + if (order.IsPostOnly == true) payload["timeInForce"] += "boc"; // timeInForce enum values: gtc - good till cancel,boc - book or cancel (also called as post only, or book only), ioc - immediate or cancel, fok - fill or kill + + order.ExtraParameters.CopyTo(payload); JToken obj = await MakeJsonRequestAsync("/order/orders/place", PrivateUrlV1, payload, "POST"); order.Amount = outputQuantity; diff --git a/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs b/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs index d3c45ca6..95d5ca13 100644 --- a/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs @@ -776,6 +776,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd if (order.Price == null) throw new ArgumentNullException(nameof(order.Price)); payload.Add("price", Math.Round(order.Price.Value, precision).ToStringInvariant()); } + if (order.IsPostOnly == true) payload["oflags"] = "post"; // post-only order (available when ordertype = limit) order.ExtraParameters.CopyTo(payload); JToken token = await MakeJsonRequestAsync("/0/private/AddOrder", null, payload); diff --git a/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs b/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs index ff6877c8..9f829a02 100644 --- a/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs @@ -408,6 +408,7 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd { payload["type"] = "limit"; payload["price"] = order.Price.ToStringInvariant(); + if (order.IsPostOnly != null) payload["postOnly"] = order.IsPostOnly; // [Optional] Post only flag, invalid when timeInForce is IOC or FOK } order.ExtraParameters.CopyTo(payload); diff --git a/src/ExchangeSharp/API/Exchanges/LBank/ExchangeLBankAPI.cs b/src/ExchangeSharp/API/Exchanges/LBank/ExchangeLBankAPI.cs index 361a46f3..5936860e 100644 --- a/src/ExchangeSharp/API/Exchanges/LBank/ExchangeLBankAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/LBank/ExchangeLBankAPI.cs @@ -296,6 +296,7 @@ protected override async Task> OnGetAmountsAsync() //PlaceOrder 9 protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); Dictionary payload = new Dictionary { { "amount", order.Amount }, diff --git a/src/ExchangeSharp/API/Exchanges/Livecoin/ExchangeLivecoinAPI.cs b/src/ExchangeSharp/API/Exchanges/Livecoin/ExchangeLivecoinAPI.cs index e8709eb2..b0dc1cb6 100644 --- a/src/ExchangeSharp/API/Exchanges/Livecoin/ExchangeLivecoinAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Livecoin/ExchangeLivecoinAPI.cs @@ -265,7 +265,8 @@ protected override async Task> OnGetOpenOrderDe protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { - var payload = await GetNoncePayloadAsync(); + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); + var payload = await GetNoncePayloadAsync(); string orderType = "/exchange/"; if (order.OrderType == OrderType.Market) { diff --git a/src/ExchangeSharp/API/Exchanges/NDAX/ExchangeNDAXAPI.cs b/src/ExchangeSharp/API/Exchanges/NDAX/ExchangeNDAXAPI.cs index c0fc332d..b8547931 100644 --- a/src/ExchangeSharp/API/Exchanges/NDAX/ExchangeNDAXAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/NDAX/ExchangeNDAXAPI.cs @@ -151,7 +151,8 @@ protected override async Task> OnGetAmountsAvailable protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { - var orderType = 0; + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); + var orderType = 0; var side = order.IsBuy? 0: 1; switch (order.OrderType) { diff --git a/src/ExchangeSharp/API/Exchanges/OKGroup/OKGroupCommon.cs b/src/ExchangeSharp/API/Exchanges/OKGroup/OKGroupCommon.cs index 7046586f..0b62cf3a 100644 --- a/src/ExchangeSharp/API/Exchanges/OKGroup/OKGroupCommon.cs +++ b/src/ExchangeSharp/API/Exchanges/OKGroup/OKGroupCommon.cs @@ -440,8 +440,9 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd { payload["price"] = outputPrice; payload["amount"] = outputQuantity; - } - order.ExtraParameters.CopyTo(payload); + if (order.IsPostOnly == true) payload["order_type"] = "1"; // Specify 0: Normal order (Unfilled and 0 imply normal limit order) 1: Post only 2: Fill or Kill 3: Immediate Or Cancel + } + order.ExtraParameters.CopyTo(payload); JToken obj = await MakeJsonRequestAsync("/trade.do", BaseUrl, payload, "POST"); order.Amount = outputQuantity; diff --git a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs index 3ea4184c..16be4aef 100644 --- a/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs @@ -782,7 +782,8 @@ protected override async Task OnPlaceOrderAsync(ExchangeOrd "rate", orderPrice.ToStringInvariant(), "amount", orderAmount.ToStringInvariant() }; - foreach (KeyValuePair kv in order.ExtraParameters) + if (order.IsPostOnly != null) { orderParams.Add("postOnly"); orderParams.Add(order.IsPostOnly.Value ? "1" : "0"); } // (optional) Set to "1" if you want this sell order to only be placed if no portion of it fills immediately. + foreach (KeyValuePair kv in order.ExtraParameters) { orderParams.Add(kv.Key); orderParams.Add(kv.Value); diff --git a/src/ExchangeSharp/API/Exchanges/Yobit/ExchangeYobitAPI.cs b/src/ExchangeSharp/API/Exchanges/Yobit/ExchangeYobitAPI.cs index fe467e03..1cc792fd 100644 --- a/src/ExchangeSharp/API/Exchanges/Yobit/ExchangeYobitAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Yobit/ExchangeYobitAPI.cs @@ -250,7 +250,8 @@ protected override async Task> OnGetOpenOrderDe protected override async Task OnPlaceOrderAsync(ExchangeOrderRequest order) { - var payload = await GetNoncePayloadAsync(); + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); + var payload = await GetNoncePayloadAsync(); payload.Add("method", "Trade"); payload.Add("pair", order.MarketSymbol); payload.Add("type", order.IsBuy ? "buy" : "sell"); diff --git a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs index c58d11d3..f166ec55 100644 --- a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPI.cs @@ -910,6 +910,7 @@ public virtual async Task> GetMarginAmountsAvailable public virtual async Task PlaceOrderAsync(ExchangeOrderRequest order) { // *NOTE* do not wrap in CacheMethodCall + if (order.IsPostOnly != null) throw new NotImplementedException("Post Only orders are not supported by this exchange or not implemented in ExchangeSharp. Please submit a PR if you are interested in this feature."); await new SynchronizationContextRemover(); order.MarketSymbol = NormalizeMarketSymbol(order.MarketSymbol); return await OnPlaceOrderAsync(order); diff --git a/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs b/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs index 3931cddf..0587a679 100644 --- a/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/_Base/IExchangeAPI.cs @@ -203,11 +203,11 @@ public interface IExchangeAPI : IDisposable, IBaseAPI, IOrderBookProvider Task PlaceOrdersAsync(params ExchangeOrderRequest[] orders); /// - /// Get details of an order + /// Get details of an order, searching by order id /// - /// order id + /// order id to search for (either server assigned id or client provided id /// Market Symbol - /// + /// Whether the order id parameter is the server assigned id or client provided id /// Order details Task GetOrderDetailsAsync(string orderId, string? marketSymbol = null, bool isClientOrderId = false); diff --git a/src/ExchangeSharp/Model/ExchangeOrderRequest.cs b/src/ExchangeSharp/Model/ExchangeOrderRequest.cs index 41e00dcb..af40af6e 100644 --- a/src/ExchangeSharp/Model/ExchangeOrderRequest.cs +++ b/src/ExchangeSharp/Model/ExchangeOrderRequest.cs @@ -75,12 +75,18 @@ public class ExchangeOrderRequest /// public OrderType OrderType { get; set; } = OrderType.Limit; - /// - /// Additional order parameters specific to the exchange that don't fit in common order properties. These will be forwarded on to the exchange as key=value pairs. - /// Not all exchanges will use this dictionary. - /// These are added after all other parameters and will replace existing properties, such as order type. - /// - public Dictionary ExtraParameters { get; private set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + /// + /// a.k.a. MakerOnly or BookOrCancel or PendingOrCancelled. Whether the order is "Post Only" (limit order will be added to the order book and not match with a pre-existing order) + /// To leave unspecified, set to null + /// + public bool? IsPostOnly { get; set; } + + /// + /// Additional order parameters specific to the exchange that don't fit in common order properties. These will be forwarded on to the exchange as key=value pairs. + /// Not all exchanges will use this dictionary. + /// These are added after all other parameters and will replace existing properties, such as order type. + /// + public Dictionary ExtraParameters { get; private set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// Return a rounded amount if needed @@ -112,5 +118,5 @@ public enum OrderType /// A stop order, you will sell if price reaches a low enough level down to a limit /// Stop - } + } }