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  /// Market name
36  /// </summary>
37  protected virtual string MarketName => Market.Binance;
38 
39  /// <summary>
40  /// Gets a map of the default markets to be used for each security type
41  /// </summary>
42  public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Binance);
43 
44  /// <summary>
45  /// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
46  /// </summary>
47  /// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
48  public BinanceBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
49  {
50  }
51 
52  /// <summary>
53  /// Binance global leverage rule
54  /// </summary>
55  /// <param name="security"></param>
56  /// <returns></returns>
57  public override decimal GetLeverage(Security security)
58  {
59  if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
60  {
61  return 1m;
62  }
63 
64  return security.Symbol.SecurityType == SecurityType.CryptoFuture ? _defaultFutureLeverage : _defaultLeverage;
65  }
66 
67  /// <summary>
68  /// Get the benchmark for this model
69  /// </summary>
70  /// <param name="securities">SecurityService to create the security with if needed</param>
71  /// <returns>The benchmark for this brokerage</returns>
72  public override IBenchmark GetBenchmark(SecurityManager securities)
73  {
74  var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
75  return SecurityBenchmark.CreateInstance(securities, symbol);
76  }
77 
78  /// <summary>
79  /// Provides Binance fee model
80  /// </summary>
81  /// <param name="security"></param>
82  /// <returns></returns>
83  public override IFeeModel GetFeeModel(Security security)
84  {
85  return new BinanceFeeModel();
86  }
87 
88  /// <summary>
89  /// Binance does not support update of orders
90  /// </summary>
91  /// <param name="security">The security of the order</param>
92  /// <param name="order">The order to be updated</param>
93  /// <param name="request">The requested update to be made to the order</param>
94  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
95  /// <returns>Binance does not support update of orders, so it will always return false</returns>
96  public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
97  {
98  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
99  return false;
100  }
101 
102  /// <summary>
103  /// Returns true if the brokerage could accept this order. This takes into account
104  /// order type, security type, and order size limits.
105  /// </summary>
106  /// <remarks>
107  /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
108  /// </remarks>
109  /// <param name="security">The security of the order</param>
110  /// <param name="order">The order to be processed</param>
111  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
112  /// <returns>True if the brokerage could process the order, false otherwise</returns>
113  public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
114  {
115  message = null;
116 
117  // Binance API provides minimum order size in quote currency
118  // and hence we have to check current order size using available price and order quantity
119  var quantityIsValid = true;
120  decimal price;
121  switch (order)
122  {
123  case LimitOrder limitOrder:
124  quantityIsValid &= IsOrderSizeLargeEnough(limitOrder.LimitPrice);
125  price = limitOrder.LimitPrice;
126  break;
127  case MarketOrder:
128  if (!security.HasData)
129  {
130  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
131  Messages.DefaultBrokerageModel.NoDataForSymbol);
132 
133  return false;
134  }
135 
136  price = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
137  quantityIsValid &= IsOrderSizeLargeEnough(price);
138  break;
139  case StopLimitOrder stopLimitOrder:
140  if (security.Symbol.SecurityType == SecurityType.CryptoFuture)
141  {
142  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
143  Messages.BinanceBrokerageModel.UnsupportedOrderTypeForSecurityType(order, security));
144  return false;
145  }
146  price = stopLimitOrder.LimitPrice;
147  quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.LimitPrice);
148  if (!quantityIsValid)
149  {
150  break;
151  }
152 
153  // Binance Trading UI requires this check too...
154  quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.StopPrice);
155  price = stopLimitOrder.StopPrice;
156  break;
157  case StopMarketOrder:
158  // despite Binance API allows you to post STOP_LOSS and TAKE_PROFIT order types
159  // they always fails with the content
160  // {"code":-1013,"msg":"Take profit orders are not supported for this symbol."}
161  // currently no symbols supporting TAKE_PROFIT or STOP_LOSS orders
162 
163  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
164  Messages.BinanceBrokerageModel.UnsupportedOrderTypeWithLinkToSupportedTypes(order, security));
165  return false;
166  default:
167  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
168  Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, new [] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit }));
169  return false;
170  }
171 
172 
173  if (!quantityIsValid)
174  {
175  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
176  Messages.DefaultBrokerageModel.InvalidOrderSize(security, order.Quantity, price));
177 
178  return false;
179  }
180 
181  if (security.Type != SecurityType.Crypto && security.Type != SecurityType.CryptoFuture)
182  {
183  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
184  Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
185 
186  return false;
187  }
188  return base.CanSubmitOrder(security, order, out message);
189 
190  bool IsOrderSizeLargeEnough(decimal price) =>
191  // if we have a minimum order size we enforce it
192  !security.SymbolProperties.MinimumOrderSize.HasValue || order.AbsoluteQuantity * price > security.SymbolProperties.MinimumOrderSize;
193  }
194 
195  protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
196  {
197  var map = DefaultMarketMap.ToDictionary();
198  map[SecurityType.Crypto] = marketName;
199  return map.ToReadOnlyDictionary();
200  }
201  }
202 }