Lean  $LEAN_TAG$
FTXBrokerageModel.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 
17 using QuantConnect.Orders;
20 using QuantConnect.Util;
21 using System.Collections.Generic;
22 using System.Linq;
23 
25 {
26  /// <summary>
27  /// FTX Brokerage model
28  /// </summary>
30  {
31  private readonly HashSet<OrderType> _supportedOrderTypes = new HashSet<OrderType>
32  {
33  OrderType.Market,
34  OrderType.Limit,
35  OrderType.StopMarket,
36  OrderType.StopLimit
37  };
38 
39  private const decimal _defaultLeverage = 3m;
40 
41  /// <summary>
42  /// market name
43  /// </summary>
44  protected virtual string MarketName => Market.FTX;
45 
46  /// <summary>
47  /// Gets a map of the default markets to be used for each security type
48  /// </summary>
49  public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.FTX);
50 
51  /// <summary>
52  /// Creates an instance of <see cref="FTXBrokerageModel"/> class
53  /// </summary>
54  /// <param name="accountType">Cash or Margin</param>
55  public FTXBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
56  {
57  }
58 
59  /// <summary>
60  /// Gets the brokerage's leverage for the specified security
61  /// </summary>
62  /// <param name="security">The security's whose leverage we seek</param>
63  /// <returns>The leverage for the specified security</returns>
64  public override decimal GetLeverage(Security security)
65  {
66  if (AccountType == AccountType.Cash)
67  {
68  return 1m;
69  }
70 
71  return _defaultLeverage;
72  }
73 
74  /// <summary>
75  /// Provides FTX fee model
76  /// </summary>
77  /// <param name="security">The security to get a fee model for</param>
78  /// <returns>The new fee model for this brokerage</returns>
79  public override IFeeModel GetFeeModel(Security security)
80  => new FTXFeeModel();
81 
82  /// <summary>
83  /// Get the benchmark for this model
84  /// </summary>
85  /// <param name="securities">SecurityService to create the security with if needed</param>
86  /// <returns>The benchmark for this brokerage</returns>
87  public override IBenchmark GetBenchmark(SecurityManager securities)
88  {
89  var symbol = Symbol.Create("BTCUSD", SecurityType.Crypto, MarketName);
90  return SecurityBenchmark.CreateInstance(securities, symbol);
91  }
92 
93  /// <summary>
94  /// Returns true if the brokerage could accept this order. This takes into account
95  /// order type, security type, and order size limits.
96  /// </summary>
97  /// <remarks>
98  /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
99  /// </remarks>
100  /// <param name="security">The security of the order</param>
101  /// <param name="order">The order to be processed</param>
102  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
103  /// <returns>True if the brokerage could process the order, false otherwise</returns>
104  public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
105  {
106  if (!IsValidOrderSize(security, order.Quantity, out message))
107  {
108  return false;
109  }
110 
111  message = null;
112 
113  // validate order type
114  if (!_supportedOrderTypes.Contains(order.Type))
115  {
116  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
117  Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
118 
119  return false;
120  }
121 
122  if (order.Type is OrderType.StopMarket or OrderType.StopLimit)
123  {
124  if (!security.HasData)
125  {
126  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
127  Messages.DefaultBrokerageModel.NoDataForSymbol);
128 
129  return false;
130  }
131 
132  var stopPrice = (order as StopMarketOrder)?.StopPrice;
133  if (!stopPrice.HasValue)
134  {
135  stopPrice = (order as StopLimitOrder)?.StopPrice;
136  }
137 
138  switch (order.Direction)
139  {
140  case OrderDirection.Sell:
141  if (stopPrice > security.BidPrice)
142  {
143  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
144  Messages.FTXBrokerageModel.TriggerPriceTooHigh);
145  }
146  break;
147 
148  case OrderDirection.Buy:
149  if (stopPrice < security.AskPrice)
150  {
151  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
152  Messages.FTXBrokerageModel.TriggerPriceTooLow);
153  }
154  break;
155  }
156 
157  if (message != null)
158  {
159  return false;
160  }
161  }
162 
163  if (security.Type != SecurityType.Crypto)
164  {
165  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
166  Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
167 
168  return false;
169  }
170  return base.CanSubmitOrder(security, order, out message);
171  }
172 
173  /// <summary>
174  /// Please note that the order's queue priority will be reset, and the order ID of the modified order will be different from that of the original order.
175  /// Also note: this is implemented as cancelling and replacing your order.
176  /// There's a chance that the order meant to be cancelled gets filled and its replacement still gets placed.
177  /// </summary>
178  /// <param name="security">The security of the order</param>
179  /// <param name="order">The order to be updated</param>
180  /// <param name="request">The requested update to be made to the order</param>
181  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
182  /// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
183  public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
184  {
185  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
186  return false;
187  }
188 
189  protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string market)
190  {
191  var map = DefaultMarketMap.ToDictionary();
192  map[SecurityType.Crypto] = market;
193  return map.ToReadOnlyDictionary();
194  }
195  }
196 }