Lean  $LEAN_TAG$
TradierBrokerageModel.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.Collections.Generic;
18 using QuantConnect.Orders;
24 
26 {
27  /// <summary>
28  /// Provides tradier specific properties
29  /// </summary>
31  {
32  private readonly HashSet<OrderType> _supportedOrderTypes = new HashSet<OrderType>
33  {
34  OrderType.Limit,
35  OrderType.Market,
36  OrderType.StopMarket,
37  OrderType.StopLimit
38  };
39 
40  /// <summary>
41  /// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
42  /// </summary>
43  /// <param name="accountType">The type of account to be modeled, defaults to
44  /// <see cref="QuantConnect.AccountType.Margin"/></param>
45  public TradierBrokerageModel(AccountType accountType = AccountType.Margin)
46  : base(accountType)
47  {
48  }
49 
50  /// <summary>
51  /// Returns true if the brokerage could accept this order. This takes into account
52  /// order type, security type, and order size limits.
53  /// </summary>
54  /// <remarks>
55  /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
56  /// </remarks>
57  /// <param name="security">The security of the order</param>
58  /// <param name="order">The order to be processed</param>
59  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
60  /// <returns>True if the brokerage could process the order, false otherwise</returns>
61  public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
62  {
63  message = null;
64 
65  if (!_supportedOrderTypes.Contains(order.Type))
66  {
67  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
68  Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
69 
70  return false;
71  }
72 
73  var securityType = order.SecurityType;
74  if (securityType != SecurityType.Equity && securityType != SecurityType.Option)
75  {
76  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
77  Messages.TradierBrokerageModel.UnsupportedSecurityType);
78 
79  return false;
80  }
81 
82  if (order.TimeInForce is not GoodTilCanceledTimeInForce && order.TimeInForce is not DayTimeInForce)
83  {
84  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
85  Messages.TradierBrokerageModel.UnsupportedTimeInForceType);
86 
87  return false;
88  }
89 
90  if (security.Holdings.Quantity + order.Quantity < 0)
91  {
93  {
94  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "ShortOrderIsGtc", Messages.TradierBrokerageModel.ShortOrderIsGtc);
95 
96  return false;
97  }
98  else if (security.Price < 5)
99  {
100  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "SellShortOrderLastPriceBelow5", Messages.TradierBrokerageModel.SellShortOrderLastPriceBelow5);
101 
102  return false;
103  }
104  }
105 
106  if (order.AbsoluteQuantity < 1 || order.AbsoluteQuantity > 10000000)
107  {
108  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "IncorrectOrderQuantity", Messages.TradierBrokerageModel.IncorrectOrderQuantity);
109 
110  return false;
111  }
112 
113  if (!CanExecuteOrder(security, order))
114  {
115  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "ExtendedMarket",
116  Messages.TradierBrokerageModel.ExtendedMarketHoursTradingNotSupported);
117  }
118 
119  // tradier order limits
120  return true;
121  }
122 
123  /// <summary>
124  /// Returns true if the brokerage would allow updating the order as specified by the request
125  /// </summary>
126  /// <param name="security">The security of the order</param>
127  /// <param name="order">The order to be updated</param>
128  /// <param name="request">The requested update to be made to the order</param>
129  /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
130  /// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
131  public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
132  {
133  message = null;
134 
135  // Tradier doesn't allow updating order quantities
136  if (request.Quantity != null && request.Quantity != order.Quantity)
137  {
138  message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected",
139  Messages.TradierBrokerageModel.OrderQuantityUpdateNotSupported);
140 
141  return false;
142  }
143 
144  return true;
145  }
146 
147  /// <summary>
148  /// Returns true if the brokerage would be able to execute this order at this time assuming
149  /// market prices are sufficient for the fill to take place. This is used to emulate the
150  /// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
151  /// executions during extended market hours. This is not intended to be checking whether or not
152  /// the exchange is open, that is handled in the Security.Exchange property.
153  /// </summary>
154  /// <param name="security">The security being ordered</param>
155  /// <param name="order">The order to test for execution</param>
156  /// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
157  public override bool CanExecuteOrder(Security security, Order order)
158  {
159  var cache = security.GetLastData();
160  if (cache == null)
161  {
162  return false;
163  }
164 
165  // tradier doesn't support after hours trading
166  if (!security.Exchange.IsOpenDuringBar(cache.Time, cache.EndTime, false))
167  {
168  return false;
169  }
170  return true;
171  }
172 
173  /// <summary>
174  /// Applies the split to the specified order ticket
175  /// </summary>
176  /// <param name="tickets">The open tickets matching the split event</param>
177  /// <param name="split">The split event data</param>
178  public override void ApplySplit(List<OrderTicket> tickets, Split split)
179  {
180  // tradier cancels reverse splits
181  var splitFactor = split.SplitFactor;
182  if (splitFactor > 1.0m)
183  {
184  tickets.ForEach(ticket => ticket.Cancel(Messages.TradierBrokerageModel.OpenOrdersCancelOnReverseSplitSymbols));
185  }
186  else
187  {
188  base.ApplySplit(tickets, split);
189  }
190  }
191 
192  /// <summary>
193  /// Gets a new fee model that represents this brokerage's fee structure
194  /// </summary>
195  /// <param name="security">The security to get a fee model for</param>
196  /// <returns>The new fee model for this brokerage</returns>
197  public override IFeeModel GetFeeModel(Security security)
198  {
199  // Trading stocks at Tradier Brokerage is free
200  return new ConstantFeeModel(0m);
201  }
202  }
203 }