diff --git a/Common/Brokerages/BinanceBrokerageModel.cs b/Common/Brokerages/BinanceBrokerageModel.cs index 488341025a0f..7c23bfb76244 100644 --- a/Common/Brokerages/BinanceBrokerageModel.cs +++ b/Common/Brokerages/BinanceBrokerageModel.cs @@ -31,6 +31,11 @@ public class BinanceBrokerageModel : DefaultBrokerageModel private const decimal _defaultLeverage = 3; private const decimal _defaultFutureLeverage = 25; + /// + /// The base Binance API endpoint URL. + /// + protected virtual string BaseApiEndpoint => "https://api.binance.com/api/v3"; + /// /// Market name /// @@ -154,18 +159,24 @@ public override bool CanSubmitOrder(Security security, Order order, out Brokerag quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.StopPrice); price = stopLimitOrder.StopPrice; break; - case StopMarketOrder: - // despite Binance API allows you to post STOP_LOSS and TAKE_PROFIT order types - // they always fails with the content - // {"code":-1013,"msg":"Take profit orders are not supported for this symbol."} - // currently no symbols supporting TAKE_PROFIT or STOP_LOSS orders + case StopMarketOrder stopMarketOrder: + if (security.Symbol.SecurityType != SecurityType.CryptoFuture) + { + // despite Binance API allows you to post STOP_LOSS and TAKE_PROFIT order types + // they always fails with the content + // {"code":-1013,"msg":"Take profit orders are not supported for this symbol."} + // currently no symbols supporting TAKE_PROFIT or STOP_LOSS orders - message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", - Messages.BinanceBrokerageModel.UnsupportedOrderTypeWithLinkToSupportedTypes(order, security)); - return false; + message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", + Messages.BinanceBrokerageModel.UnsupportedOrderTypeWithLinkToSupportedTypes(BaseApiEndpoint, order, security)); + return false; + } + quantityIsValid &= IsOrderSizeLargeEnough(stopMarketOrder.StopPrice); + price = stopMarketOrder.StopPrice; + break; default: message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", - Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, new [] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit })); + Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, new[] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit })); return false; } diff --git a/Common/Brokerages/BinanceUSBrokerageModel.cs b/Common/Brokerages/BinanceUSBrokerageModel.cs index 1493f51d36dd..c68314e2187f 100644 --- a/Common/Brokerages/BinanceUSBrokerageModel.cs +++ b/Common/Brokerages/BinanceUSBrokerageModel.cs @@ -24,6 +24,11 @@ namespace QuantConnect.Brokerages /// public class BinanceUSBrokerageModel : BinanceBrokerageModel { + /// + /// The base Binance Futures API endpoint URL. + /// + protected override string BaseApiEndpoint => "https://api.binance.us/api/v3"; + /// /// Market name /// diff --git a/Common/Messages/Messages.Brokerages.cs b/Common/Messages/Messages.Brokerages.cs index 95d0b39a3449..e045cb9bb516 100644 --- a/Common/Messages/Messages.Brokerages.cs +++ b/Common/Messages/Messages.Brokerages.cs @@ -195,10 +195,9 @@ public static string UnsupportedOrderTypeForSecurityType(Orders.Order order, Sec /// security. The message also contains a link to the supported order types in Binance /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string UnsupportedOrderTypeWithLinkToSupportedTypes(Orders.Order order, Securities.Security security) + public static string UnsupportedOrderTypeWithLinkToSupportedTypes(string baseApiEndpoint, Orders.Order order, Securities.Security security) { - return Invariant($@"{order.Type} orders are not supported for this symbol. Please check 'https://api.binance.com/api/v3/exchangeInfo?symbol={ - security.SymbolProperties.MarketTicker}' to see supported order types."); + return Invariant($@"{order.Type} orders are not supported for this symbol. Please check '{baseApiEndpoint}/exchangeInfo?symbol={security.SymbolProperties.MarketTicker}' to see supported order types."); } } diff --git a/Tests/Common/Brokerages/BinanceBrokerageModelTests.cs b/Tests/Common/Brokerages/BinanceBrokerageModelTests.cs index 478d9541cf0d..241bfc6e6500 100644 --- a/Tests/Common/Brokerages/BinanceBrokerageModelTests.cs +++ b/Tests/Common/Brokerages/BinanceBrokerageModelTests.cs @@ -14,14 +14,14 @@ */ using Moq; +using System; using NUnit.Framework; +using QuantConnect.Orders; +using QuantConnect.Securities; using QuantConnect.Brokerages; +using QuantConnect.Orders.Fees; using QuantConnect.Data.Market; -using QuantConnect.Orders; using QuantConnect.Tests.Brokerages; -using System; -using QuantConnect.Orders.Fees; -using QuantConnect.Securities; namespace QuantConnect.Tests.Common.Brokerages { @@ -121,22 +121,44 @@ public void CannotSubmitMarketOrder_IfPriceNotInitialized() Assert.NotNull(message); } - [Test] - public void CannotSubmitStopMarketOrder_Always() + [TestCase(nameof(BinanceBrokerageModel), SecurityType.Crypto, false)] + [TestCase(nameof(BinanceUSBrokerageModel), SecurityType.Crypto, false)] + [TestCase(nameof(BinanceFuturesBrokerageModel), SecurityType.CryptoFuture, true)] + [TestCase(nameof(BinanceCoinFuturesBrokerageModel), SecurityType.CryptoFuture, true)] + public void CannotSubmitStopMarketOrder_Always(string binanceBrokerageMode, SecurityType securityType, bool isCanSubmit) { + var binanceBrokerageModel = binanceBrokerageMode switch + { + "BinanceBrokerageModel" => new BinanceBrokerageModel(), + "BinanceUSBrokerageModel" => new BinanceUSBrokerageModel(), + "BinanceFuturesBrokerageModel" => new BinanceFuturesBrokerageModel(AccountType.Margin), + "BinanceCoinFuturesBrokerageModel" => new BinanceCoinFuturesBrokerageModel(AccountType.Margin), + _ => throw new ArgumentException($"Invalid binanceBrokerageModel value: '{binanceBrokerageMode}'.") + }; + var order = new Mock { Object = { - Quantity = 100000, - StopPrice = 100000 + StopPrice = 3_000 } }; + order.Setup(mock => mock.Quantity).Returns(1); - var security = TestsHelpers.GetSecurity(symbol: _btceur.Value, market: _btceur.ID.Market, quoteCurrency: "EUR"); - Assert.AreEqual(false, BinanceBrokerageModel.CanSubmitOrder(security, order.Object, out var message)); - Assert.NotNull(message); + var ETHUSDT = Symbol.Create("ETHUSDT", securityType, Market.Binance); + + var security = TestsHelpers.GetSecurity(securityType: ETHUSDT.SecurityType, symbol: ETHUSDT.Value, market: ETHUSDT.ID.Market, quoteCurrency: "USDT"); + + Assert.AreEqual(isCanSubmit, binanceBrokerageModel.CanSubmitOrder(security, order.Object, out var message)); + if (isCanSubmit) + { + Assert.IsNull(message); + } + else + { + Assert.NotNull(message); + } } [Test]