Lean  $LEAN_TAG$
BinanceBrokerageModel.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System.Linq;
17 using QuantConnect.Util;
18 using QuantConnect.Orders;
22 using System.Collections.Generic;
23 
25 {
26  /// <summary>
27  /// Provides Binance specific properties
28  /// </summary>
30  {
31  private const decimal _defaultLeverage = 3;
32  private const decimal _defaultFutureLeverage = 25;
33 
34  /// <summary>
35  /// The base Binance API endpoint URL.
36  /// </summary>
37  protected virtual string BaseApiEndpoint => "https://api.binance.com/api/v3";
38 
39  /// <summary>
40  /// Market name
41  /// </summary>
42  protected virtual string MarketName => Market.Binance;
43 
44  /// <summary>
45  /// Gets a map of the default markets to be used for each security type
46  /// </summary>
47  public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Binance);
48 
49  /// <summary>
50  /// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
51  /// </summary>
52  /// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
53  public BinanceBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
54  {
55  }
56 
57  /// <summary>
58  /// Binance global leverage rule
59  /// </summary>
60  /// <param name="security"></param>
61  /// <returns></returns>
62  public override decimal GetLeverage(Security security)
63  {
64  if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
65  {
66  return 1m;
67  }
68 
69  return security.Symbol.SecurityType == SecurityType.CryptoFuture ? _defaultFutureLeverage : _defaultLeverage;
70  }
71 
72  /// <summary>
73  /// Get the benchmark for this model
74  /// </summary>
75  /// <param name="securities">SecurityService to create the security with if needed</param>
76  /// <returns>The benchmark for this brokerage</returns>
77  public override IBenchmark GetBenchmark(SecurityManager securities)
78  {
79  var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
80  return SecurityBenchmark.CreateInstance(securities, symbol);
81  }
82 
83  /// <summary>
84  /// Provides Binance fee model
85  /// </summary>
86  /// <param name="security"></param>
87  /// <returns></returns>
88  public override IFeeModel GetFeeModel(Security security)
89  {
90  return new BinanceFeeModel();
91  }
92 
93  /// <summary>
94  /// Binance does not support update of orders
95  /// </summary>
96  /// <param name="security">The security of the order</param>
97  /// <param name="order">The order to be updated</param>
98  /// <param name="request">The requested update to be made to the order</param>
99  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
100  /// <returns>Binance does not support update of orders, so it will always return false</returns>
101  public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
102  {
104  return false;
105  }
106 
107  /// <summary>
108  /// Returns true if the brokerage could accept this order. This takes into account
109  /// order type, security type, and order size limits.
110  /// </summary>
111  /// <remarks>
112  /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
113  /// </remarks>
114  /// <param name="security">The security of the order</param>
115  /// <param name="order">The order to be processed</param>
116  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
117  /// <returns>True if the brokerage could process the order, false otherwise</returns>
118  public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
119  {
120  message = null;
121 
122  // Binance API provides minimum order size in quote currency
123  // and hence we have to check current order size using available price and order quantity
124  var quantityIsValid = true;
125  decimal price;
126  switch (order)
127  {
128  case LimitOrder limitOrder:
129  quantityIsValid &= IsOrderSizeLargeEnough(limitOrder.LimitPrice);
130  price = limitOrder.LimitPrice;
131  break;
132  case MarketOrder:
133  if (!security.HasData)
134  {
135  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
137 
138  return false;
139  }
140 
141  price = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
142  quantityIsValid &= IsOrderSizeLargeEnough(price);
143  break;
144  case StopLimitOrder stopLimitOrder:
145  if (security.Symbol.SecurityType == SecurityType.CryptoFuture)
146  {
147  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
149  return false;
150  }
151  price = stopLimitOrder.LimitPrice;
152  quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.LimitPrice);
153  if (!quantityIsValid)
154  {
155  break;
156  }
157 
158  // Binance Trading UI requires this check too...
159  quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.StopPrice);
160  price = stopLimitOrder.StopPrice;
161  break;
162  case StopMarketOrder stopMarketOrder:
163  if (security.Symbol.SecurityType != SecurityType.CryptoFuture)
164  {
165  // despite Binance API allows you to post STOP_LOSS and TAKE_PROFIT order types
166  // they always fails with the content
167  // {"code":-1013,"msg":"Take profit orders are not supported for this symbol."}
168  // currently no symbols supporting TAKE_PROFIT or STOP_LOSS orders
169 
170  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
172  return false;
173  }
174  quantityIsValid &= IsOrderSizeLargeEnough(stopMarketOrder.StopPrice);
175  price = stopMarketOrder.StopPrice;
176  break;
177  default:
178  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
179  Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, new[] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit }));
180  return false;
181  }
182 
183 
184  if (!quantityIsValid)
185  {
186  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
187  Messages.DefaultBrokerageModel.InvalidOrderSize(security, order.Quantity, price));
188 
189  return false;
190  }
191 
192  if (security.Type != SecurityType.Crypto && security.Type != SecurityType.CryptoFuture)
193  {
194  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
196 
197  return false;
198  }
199  return base.CanSubmitOrder(security, order, out message);
200 
201  bool IsOrderSizeLargeEnough(decimal price) =>
202  // if we have a minimum order size we enforce it
203  !security.SymbolProperties.MinimumOrderSize.HasValue || order.AbsoluteQuantity * price > security.SymbolProperties.MinimumOrderSize;
204  }
205 
206  /// <summary>
207  /// Returns a readonly dictionary of binance default markets
208  /// </summary>
209  protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
210  {
211  var map = DefaultMarketMap.ToDictionary();
212  map[SecurityType.Crypto] = marketName;
213  return map.ToReadOnlyDictionary();
214  }
215  }
216 }