Lean  $LEAN_TAG$
BybitBrokerageModel.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;
17 using System.Collections.Generic;
18 using System.Linq;
20 using QuantConnect.Orders;
24 using QuantConnect.Util;
25 
26 namespace QuantConnect.Brokerages;
27 
28 /// <summary>
29 /// Provides Bybit specific properties
30 /// </summary>
31 public class BybitBrokerageModel : DefaultBrokerageModel
32 {
33  /// <summary>
34  /// Market name
35  /// </summary>
36  protected virtual string MarketName => Market.Bybit;
37 
38  /// <summary>
39  /// Gets a map of the default markets to be used for each security type
40  /// </summary>
41  public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Bybit);
42 
43  /// <summary>
44  /// Initializes a new instance of the <see cref="BybitBrokerageModel"/> class
45  /// </summary>
46  /// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
47  public BybitBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
48  {
49  }
50 
51  /// <summary>
52  /// Bybit global leverage rule
53  /// </summary>
54  /// <param name="security"></param>
55  /// <returns></returns>
56  public override decimal GetLeverage(Security security)
57  {
58  if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
59  {
60  return 1m;
61  }
62 
63  return 10;
64  }
65 
66  /// <summary>
67  /// Provides Bybit fee model
68  /// </summary>
69  /// <param name="security"></param>
70  /// <returns></returns>
71  public override IFeeModel GetFeeModel(Security security)
72  {
73  return security.Type switch
74  {
75  SecurityType.Crypto => new BybitFeeModel(),
76  SecurityType.CryptoFuture => new BybitFuturesFeeModel(),
77  SecurityType.Base => base.GetFeeModel(security),
78  _ => throw new ArgumentOutOfRangeException(nameof(security), security, $"Not supported security type {security.Type}")
79  };
80  }
81 
82  /// <summary>
83  /// Gets a new margin interest rate model for the security
84  /// </summary>
85  /// <param name="security">The security to get a margin interest rate model for</param>
86  /// <returns>The margin interest rate model for this brokerage</returns>
88  {
89  // only applies for perpetual futures
90  if (security.Type == SecurityType.CryptoFuture &&
91  security.Symbol.ID.Date == SecurityIdentifier.DefaultDate)
92  {
94  }
95 
96  return base.GetMarginInterestRateModel(security);
97  }
98 
99  /// <summary>
100  /// Get the benchmark for this model
101  /// </summary>
102  /// <param name="securities">SecurityService to create the security with if needed</param>
103  /// <returns>The benchmark for this brokerage</returns>
104  public override IBenchmark GetBenchmark(SecurityManager securities)
105  {
106  var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
107  return SecurityBenchmark.CreateInstance(securities, symbol);
108  //todo default conversion?
109  }
110 
111  /// <summary>
112  /// Returns true if the brokerage could accept this order update. This takes into account
113  /// order type, security type, and order size limits. Bybit can only update inverse, linear, and option orders
114  /// </summary>
115  /// <param name="security">The security of the order</param>
116  /// <param name="order">The order to be updated</param>
117  /// <param name="request">The requested update to be made to the order</param>
118  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
119  /// <returns>True if the brokerage could update the order, false otherwise</returns>
120  public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request,
121  out BrokerageMessageEvent message)
122  {
123  //can only update linear, inverse, and options
124  if (security.Type != SecurityType.CryptoFuture)
125  {
126  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
127  Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
128  return false;
129  }
130 
131  if (order.Status is not (OrderStatus.New or OrderStatus.PartiallyFilled or OrderStatus.Submitted or OrderStatus.UpdateSubmitted))
132  {
133  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
134  $"Order with status {order.Status} can't be modified");
135  return false;
136  }
137 
138  if (request.Quantity.HasValue && !IsOrderSizeLargeEnough(security, Math.Abs(request.Quantity.Value)))
139  {
140  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
141  Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, request.Quantity.Value));
142  return false;
143  }
144 
145  message = null;
146  return true;
147  }
148 
149  /// <summary>
150  /// Returns true if the brokerage could accept this order. This takes into account
151  /// order type, security type, and order size limits.
152  /// </summary>
153  /// <remarks>
154  /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
155  /// </remarks>
156  /// <param name="security">The security of the order</param>
157  /// <param name="order">The order to be processed</param>
158  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
159  /// <returns>True if the brokerage could process the order, false otherwise</returns>
160  public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
161  {
162  if (security.Type != SecurityType.Crypto && security.Type != SecurityType.CryptoFuture && security.Type != SecurityType.Base)
163  {
164  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
165  Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
166 
167  return false;
168  }
169 
170  message = null;
171  bool quantityIsValid;
172 
173  switch (order)
174  {
175  case StopLimitOrder:
176  case StopMarketOrder:
177  case LimitOrder:
178  case MarketOrder:
179  quantityIsValid = IsOrderSizeLargeEnough(security, Math.Abs(order.Quantity));
180  break;
181  default:
182  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
183  Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order,
184  new[] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit }));
185  return false;
186  }
187 
188  if (!quantityIsValid)
189  {
190  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
191  Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, order.Quantity));
192 
193  return false;
194  }
195 
196  return base.CanSubmitOrder(security, order, out message);
197  }
198 
199  /// <summary>
200  /// Returns true if the order size is large enough for the given security.
201  /// </summary>
202  /// <param name="security">The security of the order</param>
203  /// <param name="orderQuantity">The order quantity</param>
204  /// <returns>True if the order size is large enough, false otherwise</returns>
205  protected virtual bool IsOrderSizeLargeEnough(Security security, decimal orderQuantity)
206  {
207  return !security.SymbolProperties.MinimumOrderSize.HasValue ||
208  orderQuantity > security.SymbolProperties.MinimumOrderSize;
209  }
210 
211  private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
212  {
213  var map = DefaultMarketMap.ToDictionary();
214  map[SecurityType.Crypto] = marketName;
215  map[SecurityType.CryptoFuture] = marketName;
216  return map.ToReadOnlyDictionary();
217  }
218 }