Lean  $LEAN_TAG$
Messages.Orders.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;
17 using System.Linq;
18 using System.Runtime.CompilerServices;
19 
20 using QuantConnect.Orders;
22 
23 using static QuantConnect.StringExtensions;
24 
25 namespace QuantConnect
26 {
27  /// <summary>
28  /// Provides user-facing message construction methods and static messages for the <see cref="Orders"/> namespace
29  /// </summary>
30  public static partial class Messages
31  {
32  /// <summary>
33  /// Provides user-facing messages for the <see cref="Orders.CancelOrderRequest"/> class and its consumers or related classes
34  /// </summary>
35  public static class CancelOrderRequest
36  {
37  [MethodImpl(MethodImplOptions.AggressiveInlining)]
38  public static string ToString(Orders.CancelOrderRequest request)
39  {
40  return Invariant($@"{request.Time.ToStringInvariant()} UTC: Cancel Order: ({request.OrderId}) - {
41  request.Tag} Status: {request.Status}");
42  }
43  }
44 
45  /// <summary>
46  /// Provides user-facing messages for the <see cref="Orders.GroupOrderExtensions"/> class and its consumers or related classes
47  /// </summary>
48  public static class GroupOrderExtensions
49  {
50  [MethodImpl(MethodImplOptions.AggressiveInlining)]
51  public static string InsufficientBuyingPowerForOrders(Dictionary<Orders.Order, Securities.Security> securities,
52  HasSufficientBuyingPowerForOrderResult hasSufficientBuyingPowerResult)
53  {
54  var ids = string.Join(",", securities.Keys.Select(o => o.Id));
55  var values = string.Join(",", securities.Select(o => o.Key.GetValue(o.Value).SmartRounding()));
56  return $@"Order Error: ids: [{ids}], Insufficient buying power to complete orders (Value:[{values}]), Reason: {
57  hasSufficientBuyingPowerResult.Reason}.";
58  }
59  }
60 
61  /// <summary>
62  /// Provides user-facing messages for the <see cref="Orders.LimitIfTouchedOrder"/> class and its consumers or related classes
63  /// </summary>
64  public static class LimitIfTouchedOrder
65  {
66  [MethodImpl(MethodImplOptions.AggressiveInlining)]
67  public static string Tag(Orders.LimitIfTouchedOrder order)
68  {
69  // No additional information to display
70  return string.Empty;
71  }
72 
73  [MethodImpl(MethodImplOptions.AggressiveInlining)]
74  public static string ToString(Orders.LimitIfTouchedOrder order)
75  {
76  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(order.PriceCurrency);
77  return Invariant($@"{Order.ToString(order)} at trigger {currencySymbol}{order.TriggerPrice.SmartRounding()
78  } limit {currencySymbol}{order.LimitPrice.SmartRounding()}");
79  }
80  }
81 
82  /// <summary>
83  /// Provides user-facing messages for the <see cref="Orders.LimitOrder"/> class and its consumers or related classes
84  /// </summary>
85  public static class LimitOrder
86  {
87  [MethodImpl(MethodImplOptions.AggressiveInlining)]
88  public static string Tag(Orders.LimitOrder order)
89  {
90  // No additional information to display
91  return string.Empty;
92  }
93 
94  [MethodImpl(MethodImplOptions.AggressiveInlining)]
95  public static string ToString(Orders.LimitOrder order)
96  {
97  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(order.PriceCurrency);
98  return Invariant($"{Order.ToString(order)} at limit {currencySymbol}{order.LimitPrice.SmartRounding()}");
99  }
100  }
101 
102  /// <summary>
103  /// Provides user-facing messages for the <see cref="Orders.Order"/> class and its consumers or related classes
104  /// </summary>
105  public static class Order
106  {
107  [MethodImpl(MethodImplOptions.AggressiveInlining)]
108  public static string ToString(Orders.Order order)
109  {
110  var tag = string.IsNullOrEmpty(order.Tag) ? string.Empty : $": {order.Tag}";
111  return Invariant($@"OrderId: {order.Id} (BrokerId: {string.Join(",", order.BrokerId)}) {order.Status} {
112  order.Type} order for {order.Quantity} unit{(order.Quantity == 1 ? "" : "s")} of {order.Symbol}{tag}");
113  }
114  }
115 
116  /// <summary>
117  /// Provides user-facing messages for the <see cref="Orders.OrderEvent"/> class and its consumers or related classes
118  /// </summary>
119  public static class OrderEvent
120  {
121  [MethodImpl(MethodImplOptions.AggressiveInlining)]
122  public static string ToString(Orders.OrderEvent orderEvent)
123  {
124  var message = Invariant($@"Time: {orderEvent.UtcTime} OrderID: {orderEvent.OrderId} EventID: {
125  orderEvent.Id} Symbol: {orderEvent.Symbol.Value} Status: {orderEvent.Status} Quantity: {orderEvent.Quantity}");
126  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(orderEvent.FillPriceCurrency);
127 
128  if (orderEvent.FillQuantity != 0)
129  {
130  message += Invariant($@" FillQuantity: {orderEvent.FillQuantity
131  } FillPrice: {currencySymbol}{orderEvent.FillPrice.SmartRounding()}");
132  }
133 
134  if (orderEvent.LimitPrice.HasValue)
135  {
136  message += Invariant($" LimitPrice: {currencySymbol}{orderEvent.LimitPrice.Value.SmartRounding()}");
137  }
138 
139  if (orderEvent.StopPrice.HasValue)
140  {
141  message += Invariant($" StopPrice: {currencySymbol}{orderEvent.StopPrice.Value.SmartRounding()}");
142  }
143 
144  if (orderEvent.TrailingAmount.HasValue)
145  {
146  var trailingAmountString = TrailingStopOrder.TrailingAmount(orderEvent.TrailingAmount.Value,
147  orderEvent.TrailingAsPercentage ?? false, currencySymbol);
148  message += $" TrailingAmount: {trailingAmountString}";
149  }
150 
151  if (orderEvent.TriggerPrice.HasValue)
152  {
153  message += Invariant($" TriggerPrice: {currencySymbol}{orderEvent.TriggerPrice.Value.SmartRounding()}");
154  }
155 
156  // attach the order fee so it ends up in logs properly.
157  if (orderEvent.OrderFee.Value.Amount != 0m)
158  {
159  message += Invariant($" OrderFee: {orderEvent.OrderFee}");
160  }
161 
162  // add message from brokerage
163  if (!string.IsNullOrEmpty(orderEvent.Message))
164  {
165  message += Invariant($" Message: {orderEvent.Message}");
166  }
167 
168  if (orderEvent.Symbol.SecurityType.IsOption())
169  {
170  message += Invariant($" IsAssignment: {orderEvent.IsAssignment}");
171  }
172 
173  return message;
174  }
175 
176  [MethodImpl(MethodImplOptions.AggressiveInlining)]
177  public static string ShortToString(Orders.OrderEvent orderEvent)
178  {
179  var message = Invariant($"{orderEvent.UtcTime} OID:{orderEvent.OrderId} {orderEvent.Symbol.Value} {orderEvent.Status} Q:{orderEvent.Quantity}");
180  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(orderEvent.FillPriceCurrency);
181 
182  if (orderEvent.FillQuantity != 0)
183  {
184  message += Invariant($" FQ:{orderEvent.FillQuantity} FP:{currencySymbol}{orderEvent.FillPrice.SmartRounding()}");
185  }
186 
187  if (orderEvent.LimitPrice.HasValue)
188  {
189  message += Invariant($" LP:{currencySymbol}{orderEvent.LimitPrice.Value.SmartRounding()}");
190  }
191 
192  if (orderEvent.StopPrice.HasValue)
193  {
194  message += Invariant($" SP:{currencySymbol}{orderEvent.StopPrice.Value.SmartRounding()}");
195  }
196 
197  if (orderEvent.TrailingAmount.HasValue)
198  {
199  var trailingAmountString = TrailingStopOrder.TrailingAmount(orderEvent.TrailingAmount.Value,
200  orderEvent.TrailingAsPercentage ?? false, currencySymbol);
201  message += $" TA: {trailingAmountString}";
202  }
203 
204  if (orderEvent.TriggerPrice.HasValue)
205  {
206  message += Invariant($" TP:{currencySymbol}{orderEvent.TriggerPrice.Value.SmartRounding()}");
207  }
208 
209  // attach the order fee so it ends up in logs properly.
210  if (orderEvent.OrderFee.Value.Amount != 0m)
211  {
212  message += Invariant($" OF:{currencySymbol}{orderEvent.OrderFee}");
213  }
214 
215  // add message from brokerage
216  if (!string.IsNullOrEmpty(orderEvent.Message))
217  {
218  message += Invariant($" M:{orderEvent.Message}");
219  }
220 
221  if (orderEvent.Symbol.SecurityType.IsOption())
222  {
223  message += Invariant($" IA:{orderEvent.IsAssignment}");
224  }
225 
226  return message;
227  }
228  }
229 
230  /// <summary>
231  /// Provides user-facing messages for the <see cref="Orders.OrderRequest"/> class and its consumers or related classes
232  /// </summary>
233  public static class OrderRequest
234  {
235  [MethodImpl(MethodImplOptions.AggressiveInlining)]
236  public static string ToString(Orders.OrderRequest request)
237  {
238  return Invariant($"{request.Time} UTC: Order: ({request.OrderId}) - {request.Tag} Status: {request.Status}");
239  }
240  }
241 
242  /// <summary>
243  /// Provides user-facing messages for the <see cref="Orders.OrderResponse"/> class and its consumers or related classes
244  /// </summary>
245  public static class OrderResponse
246  {
247  public static string DefaultErrorMessage = "An unexpected error occurred.";
248 
249  public static string UnprocessedOrderResponseErrorMessage = "The request has not yet been processed.";
250 
251  [MethodImpl(MethodImplOptions.AggressiveInlining)]
252  public static string ToString(Orders.OrderResponse response)
253  {
254  if (response == Orders.OrderResponse.Unprocessed)
255  {
256  return "Unprocessed";
257  }
258 
259  if (response.IsError)
260  {
261  return Invariant($"Error: {response.ErrorCode} - {response.ErrorMessage}");
262  }
263 
264  return "Success";
265  }
266 
267  [MethodImpl(MethodImplOptions.AggressiveInlining)]
268  public static string InvalidStatus(Orders.OrderRequest request, Orders.Order order)
269  {
270  return Invariant($"Unable to update order with id {request.OrderId} because it already has {order.Status} status.");
271  }
272 
273  [MethodImpl(MethodImplOptions.AggressiveInlining)]
274  public static string InvalidNewStatus(Orders.OrderRequest request, Orders.Order order)
275  {
276  return Invariant($@"Unable to update or cancel order with id {
277  request.OrderId} and status {order.Status} because the submit confirmation has not been received yet.");
278  }
279 
280  [MethodImpl(MethodImplOptions.AggressiveInlining)]
281  public static string UnableToFindOrder(Orders.OrderRequest request)
282  {
283  return Invariant($"Unable to locate order with id {request.OrderId}.");
284  }
285 
286  [MethodImpl(MethodImplOptions.AggressiveInlining)]
287  public static string ZeroQuantity(Orders.OrderRequest request)
288  {
289  return Invariant($"Unable to {request.OrderRequestType.ToLower()} order with id {request.OrderId} that has zero quantity.");
290  }
291 
292  [MethodImpl(MethodImplOptions.AggressiveInlining)]
293  public static string MissingSecurity(Orders.SubmitOrderRequest request)
294  {
295  return Invariant($"You haven't requested {request.Symbol} data. Add this with AddSecurity() in the Initialize() Method.");
296  }
297 
298  [MethodImpl(MethodImplOptions.AggressiveInlining)]
299  public static string WarmingUp(Orders.OrderRequest request)
300  {
301  return Invariant($@"This operation is not allowed in Initialize or during warm up: OrderRequest.{
302  request.OrderRequestType}. Please move this code to the OnWarmupFinished() method.");
303  }
304  }
305 
306  /// <summary>
307  /// Provides user-facing messages for the <see cref="Orders.OrderTicket"/> class and its consumers or related classes
308  /// </summary>
309  public static class OrderTicket
310  {
311  [MethodImpl(MethodImplOptions.AggressiveInlining)]
312  public static string GetFieldError(Orders.OrderTicket ticket, OrderField field)
313  {
314  return Invariant($"Unable to get field {field} on order of type {ticket.SubmitRequest.OrderType}");
315  }
316 
317  [MethodImpl(MethodImplOptions.AggressiveInlining)]
318  public static string CancelRequestAlreadySubmitted(Orders.OrderTicket ticket)
319  {
320  return Invariant($"Order {ticket.OrderId} has already received a cancellation request.");
321  }
322 
323  [MethodImpl(MethodImplOptions.AggressiveInlining)]
324  public static string ToString(Orders.OrderTicket ticket, Orders.Order order, int requestCount, int responseCount)
325  {
326  var counts = Invariant($"Request Count: {requestCount} Response Count: {responseCount}");
327  if (order != null)
328  {
329  return Invariant($"{ticket.OrderId}: {order} {counts}");
330  }
331 
332  return Invariant($"{ticket.OrderId}: {counts}");
333  }
334  }
335 
336  /// <summary>
337  /// Provides user-facing messages for the <see cref="Orders.StopLimitOrder"/> class and its consumers or related classes
338  /// </summary>
339  public static class StopLimitOrder
340  {
341  [MethodImpl(MethodImplOptions.AggressiveInlining)]
342  public static string Tag(Orders.StopLimitOrder order)
343  {
344  // No additional information to display
345  return string.Empty;
346  }
347 
348  [MethodImpl(MethodImplOptions.AggressiveInlining)]
349  public static string ToString(Orders.StopLimitOrder order)
350  {
351  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(order.PriceCurrency);
352  return Invariant($@"{Order.ToString(order)} at stop {currencySymbol}{order.StopPrice.SmartRounding()
353  } limit {currencySymbol}{order.LimitPrice.SmartRounding()}");
354  }
355  }
356 
357  /// <summary>
358  /// Provides user-facing messages for the <see cref="Orders.StopMarketOrder"/> class and its consumers or related classes
359  /// </summary>
360  public static class StopMarketOrder
361  {
362  [MethodImpl(MethodImplOptions.AggressiveInlining)]
363  public static string Tag(Orders.StopMarketOrder order)
364  {
365  // No additional information to display
366  return string.Empty;
367  }
368 
369  [MethodImpl(MethodImplOptions.AggressiveInlining)]
370  public static string ToString(Orders.StopMarketOrder order)
371  {
372  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(order.PriceCurrency);
373  return Invariant($"{Order.ToString(order)} at stop {currencySymbol}{order.StopPrice.SmartRounding()}");
374  }
375  }
376 
377  /// <summary>
378  /// Provides user-facing messages for the <see cref="Orders.TrailingStopOrder"/> class and its consumers or related classes
379  /// </summary>
380  public static class TrailingStopOrder
381  {
382  [MethodImpl(MethodImplOptions.AggressiveInlining)]
383  public static string Tag(Orders.TrailingStopOrder order)
384  {
385  return Invariant($"Trailing Amount: {TrailingAmount(order)}");
386  }
387 
388  [MethodImpl(MethodImplOptions.AggressiveInlining)]
389  public static string ToString(Orders.TrailingStopOrder order)
390  {
391  var currencySymbol = QuantConnect.Currencies.GetCurrencySymbol(order.PriceCurrency);
392  return Invariant($@"{Order.ToString(order)} at stop {currencySymbol}{order.StopPrice.SmartRounding()}. Trailing amount: {
393  TrailingAmountImpl(order, currencySymbol)}");
394  }
395 
396  [MethodImpl(MethodImplOptions.AggressiveInlining)]
397  public static string TrailingAmount(Orders.TrailingStopOrder order)
398  {
399  return TrailingAmountImpl(order, QuantConnect.Currencies.GetCurrencySymbol(order.PriceCurrency));
400  }
401 
402  [MethodImpl(MethodImplOptions.AggressiveInlining)]
403  public static string TrailingAmount(decimal trailingAmount, bool trailingAsPercentage, string priceCurrency)
404  {
405  return trailingAsPercentage ? Invariant($"{trailingAmount * 100}%") : Invariant($"{priceCurrency}{trailingAmount}");
406  }
407 
408  [MethodImpl(MethodImplOptions.AggressiveInlining)]
409  private static string TrailingAmountImpl(Orders.TrailingStopOrder order, string currencySymbol)
410  {
411  return TrailingAmount(order.TrailingAmount, order.TrailingAsPercentage, currencySymbol);
412  }
413  }
414 
415  /// <summary>
416  /// Provides user-facing messages for the <see cref="Orders.SubmitOrderRequest"/> class and its consumers or related classes
417  /// </summary>
418  public static class SubmitOrderRequest
419  {
420  [MethodImpl(MethodImplOptions.AggressiveInlining)]
421  public static string ToString(Orders.SubmitOrderRequest request)
422  {
423  // create a proxy order object to steal its ToString method
424  var proxy = Orders.Order.CreateOrder(request);
425  return Invariant($"{request.Time} UTC: Submit Order: ({request.OrderId}) - {proxy} {request.Tag} Status: {request.Status}");
426  }
427  }
428 
429  /// <summary>
430  /// Provides user-facing messages for the <see cref="Orders.UpdateOrderRequest"/> class and its consumers or related classes
431  /// </summary>
432  public static class UpdateOrderRequest
433  {
434  [MethodImpl(MethodImplOptions.AggressiveInlining)]
435  public static string ToString(Orders.UpdateOrderRequest request)
436  {
437  var updates = new List<string>(4);
438  if (request.Quantity.HasValue)
439  {
440  updates.Add(Invariant($"Quantity: {request.Quantity.Value}"));
441  }
442  if (request.LimitPrice.HasValue)
443  {
444  updates.Add(Invariant($"LimitPrice: {request.LimitPrice.Value.SmartRounding()}"));
445  }
446  if (request.StopPrice.HasValue)
447  {
448  updates.Add(Invariant($"StopPrice: {request.StopPrice.Value.SmartRounding()}"));
449  }
450  if (request.TrailingAmount.HasValue)
451  {
452  updates.Add(Invariant($"TrailingAmount: {request.TrailingAmount.Value.SmartRounding()}"));
453  }
454  if (request.TriggerPrice.HasValue)
455  {
456  updates.Add(Invariant($"TriggerPrice: {request.TriggerPrice.Value.SmartRounding()}"));
457  }
458 
459  return Invariant($@"{request.Time} UTC: Update Order: ({request.OrderId}) - {string.Join(", ", updates)} {
460  request.Tag} Status: {request.Status}");
461  }
462  }
463  }
464 }