Lean  $LEAN_TAG$
OrderJsonConverter.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.Linq;
18 using Newtonsoft.Json;
19 using Newtonsoft.Json.Linq;
22 
23 namespace QuantConnect.Orders
24 {
25  /// <summary>
26  /// Provides an implementation of <see cref="JsonConverter"/> that can deserialize Orders
27  /// </summary>
28  public class OrderJsonConverter : JsonConverter
29  {
30  /// <summary>
31  /// Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON.
32  /// </summary>
33  /// <value>
34  /// <c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON; otherwise, <c>false</c>.
35  /// </value>
36  public override bool CanWrite
37  {
38  get { return false; }
39  }
40 
41  /// <summary>
42  /// Determines whether this instance can convert the specified object type.
43  /// </summary>
44  /// <param name="objectType">Type of the object.</param>
45  /// <returns>
46  /// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
47  /// </returns>
48  public override bool CanConvert(Type objectType)
49  {
50  return typeof(Order).IsAssignableFrom(objectType);
51  }
52 
53  /// <summary>
54  /// Writes the JSON representation of the object.
55  /// </summary>
56  /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param><param name="value">The value.</param><param name="serializer">The calling serializer.</param>
57  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
58  {
59  throw new NotImplementedException("The OrderJsonConverter does not implement a WriteJson method;.");
60  }
61 
62  /// <summary>
63  /// Reads the JSON representation of the object.
64  /// </summary>
65  /// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param><param name="objectType">Type of the object.</param><param name="existingValue">The existing value of object being read.</param><param name="serializer">The calling serializer.</param>
66  /// <returns>
67  /// The object value.
68  /// </returns>
69  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
70  {
71  var jObject = JObject.Load(reader);
72 
73  var order = CreateOrderFromJObject(jObject);
74 
75  return order;
76  }
77 
78  /// <summary>
79  /// Create an order from a simple JObject
80  /// </summary>
81  /// <param name="jObject"></param>
82  /// <returns>Order Object</returns>
83  public static Order CreateOrderFromJObject(JObject jObject)
84  {
85  // create order instance based on order type field
86  var orderType = (OrderType)(jObject["Type"]?.Value<int>() ?? jObject["type"].Value<int>());
87  var order = CreateOrder(orderType, jObject);
88 
89  // populate common order properties
90  order.Id = jObject["Id"]?.Value<int>() ?? jObject["id"].Value<int>();
91 
92  var jsonStatus = jObject["Status"] ?? jObject["status"];
93  var jsonTime = jObject["Time"] ?? jObject["time"];
94  if (jsonStatus.Type == JTokenType.Integer)
95  {
96  order.Status = (OrderStatus)jsonStatus.Value<int>();
97  }
98  else if (jsonStatus.Type == JTokenType.Null)
99  {
100  order.Status = OrderStatus.Canceled;
101  }
102  else
103  {
104  // The `Status` tag can sometimes appear as a string of the enum value in the LiveResultPacket.
105  order.Status = (OrderStatus)Enum.Parse(typeof(OrderStatus), jsonStatus.Value<string>(), true);
106  }
107  if (jsonTime != null && jsonTime.Type != JTokenType.Null)
108  {
109  order.Time = jsonTime.Value<DateTime>();
110  }
111  else
112  {
113  // `Time` can potentially be null in some LiveResultPacket instances, but
114  // `CreatedTime` will always be there if `Time` is absent.
115  order.Time = (jObject["CreatedTime"]?.Value<DateTime>() ?? jObject["createdTime"].Value<DateTime>());
116  }
117 
118  var orderSubmissionData = jObject["OrderSubmissionData"] ?? jObject["orderSubmissionData"];
119  if (orderSubmissionData != null && orderSubmissionData.Type != JTokenType.Null)
120  {
121  var bidPrice = orderSubmissionData["BidPrice"]?.Value<decimal>() ?? orderSubmissionData["bidPrice"].Value<decimal>();
122  var askPrice = orderSubmissionData["AskPrice"]?.Value<decimal>() ?? orderSubmissionData["askPrice"].Value<decimal>();
123  var lastPrice = orderSubmissionData["LastPrice"]?.Value<decimal>() ?? orderSubmissionData["lastPrice"].Value<decimal>();
124  order.OrderSubmissionData = new OrderSubmissionData(bidPrice, askPrice, lastPrice);
125  }
126 
127  var priceAdjustmentMode = jObject["PriceAdjustmentMode"] ?? jObject["priceAdjustmentMode"];
128  if (priceAdjustmentMode != null && priceAdjustmentMode.Type != JTokenType.Null)
129  {
130  var value = priceAdjustmentMode.Value<int>();
131  order.PriceAdjustmentMode = (DataNormalizationMode)value;
132  }
133 
134  var lastFillTime = jObject["LastFillTime"] ?? jObject["lastFillTime"];
135  var lastUpdateTime = jObject["LastUpdateTime"] ?? jObject["lastUpdateTime"];
136  var canceledTime = jObject["CanceledTime"] ?? jObject["canceledTime"];
137 
138  if (canceledTime != null && canceledTime.Type != JTokenType.Null)
139  {
140  order.CanceledTime = canceledTime.Value<DateTime>();
141  }
142  if (lastFillTime != null && lastFillTime.Type != JTokenType.Null)
143  {
144  order.LastFillTime = lastFillTime.Value<DateTime>();
145  }
146  if (lastUpdateTime != null && lastUpdateTime.Type != JTokenType.Null)
147  {
148  order.LastUpdateTime = lastUpdateTime.Value<DateTime>();
149  }
150  var tag = jObject["Tag"] ?? jObject["tag"];
151  if (tag != null && tag.Type != JTokenType.Null)
152  {
153  order.Tag = tag.Value<string>();
154  }
155  else
156  {
157  order.Tag = string.Empty;
158  }
159 
160  order.Quantity = jObject["Quantity"]?.Value<decimal>() ?? jObject["quantity"].Value<decimal>();
161  var orderPrice = jObject["Price"] ?? jObject["price"];
162  if (orderPrice != null && orderPrice.Type != JTokenType.Null)
163  {
164  order.Price = orderPrice.Value<decimal>();
165  }
166  else
167  {
168  order.Price = default(decimal);
169  }
170 
171  var priceCurrency = jObject["PriceCurrency"] ?? jObject["priceCurrency"];
172  if (priceCurrency != null && priceCurrency.Type != JTokenType.Null)
173  {
174  order.PriceCurrency = priceCurrency.Value<string>();
175  }
176  order.BrokerId = jObject["BrokerId"]?.Select(x => x.Value<string>()).ToList() ?? jObject["brokerId"].Select(x => x.Value<string>()).ToList();
177  var jsonContingentId = jObject["ContingentId"] ?? jObject["contingentId"];
178  if (jsonContingentId != null && jsonContingentId.Type != JTokenType.Null)
179  {
180  order.ContingentId = jsonContingentId.Value<int>();
181  }
182 
183  var timeInForce = jObject["Properties"]?["TimeInForce"] ?? jObject["TimeInForce"] ?? jObject["Duration"];
184  if (timeInForce == null)
185  {
186  timeInForce = jObject["properties"]?["timeInForce"] ?? jObject["timeInForce"] ?? jObject["duration"];
187  }
188  order.Properties.TimeInForce = (timeInForce != null)
189  ? CreateTimeInForce(timeInForce, jObject)
191 
192  if (jObject.SelectTokens("Symbol.ID").Any())
193  {
194  var sid = SecurityIdentifier.Parse(jObject.SelectTokens("Symbol.ID").Single().Value<string>());
195  var ticker = jObject.SelectTokens("Symbol.Value").Single().Value<string>();
196  order.Symbol = new Symbol(sid, ticker);
197  }
198  else if (jObject.SelectTokens("symbol.id").Any())
199  {
200  var sid = SecurityIdentifier.Parse(jObject.SelectTokens("symbol.id").Single().Value<string>());
201  var ticker = jObject.SelectTokens("symbol.value").Single().Value<string>();
202  order.Symbol = new Symbol(sid, ticker);
203  }
204  else
205  {
206  string market = null;
207 
208  //does data have market?
209  var suppliedMarket = jObject.SelectTokens("Symbol.ID.Market") ?? jObject.SelectTokens("symbol.ID.Market");
210  if (suppliedMarket.Any())
211  {
212  market = suppliedMarket.Single().Value<string>();
213  }
214 
215  // we only get the security type if we need it, because it might not be there in other cases
216  var securityType = (SecurityType)(jObject["SecurityType"]?.Value<int>() ?? jObject["securityType"].Value<int>());
217 
218  var symbolValueUpperCase = jObject.SelectTokens("Symbol.Value");
219  var symbolValueCamelCase = jObject.SelectTokens("symbol.value");
220  string ticker = default;
221  if (symbolValueUpperCase.Any())
222  {
223  // provide for backwards compatibility
224  ticker = symbolValueUpperCase.Single().Value<string>();
225  }
226  else if (symbolValueCamelCase.Any())
227  {
228  // provide compatibility for orders in camel case
229  ticker = symbolValueCamelCase.Single().Value<string>();
230  }
231  else
232  {
233  ticker = jObject["Symbol"]?.Value<string>() ?? jObject["symbol"]?.Value<string>();
234  }
235 
236  if (market == null && !SymbolPropertiesDatabase.FromDataFolder().TryGetMarket(ticker, securityType, out market))
237  {
238  market = DefaultBrokerageModel.DefaultMarketMap[securityType];
239  }
240  order.Symbol = Symbol.Create(ticker, securityType, market);
241  }
242 
243  return order;
244  }
245 
246  /// <summary>
247  /// Creates an order of the correct type
248  /// </summary>
249  private static Order CreateOrder(OrderType orderType, JObject jObject)
250  {
251  Order order;
252  switch (orderType)
253  {
254  case OrderType.Market:
255  order = new MarketOrder();
256  break;
257 
258  case OrderType.Limit:
259  order = new LimitOrder { LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal) };
260  break;
261 
262  case OrderType.StopMarket:
263  order = new StopMarketOrder
264  {
265  StopPrice = jObject["stopPrice"]?.Value<decimal>() ?? jObject["StopPrice"]?.Value<decimal>() ?? default(decimal)
266  };
267  break;
268 
269  case OrderType.StopLimit:
270  order = new StopLimitOrder
271  {
272  LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal),
273  StopPrice = jObject["stopPrice"]?.Value<decimal>() ?? jObject["StopPrice"]?.Value<decimal>() ?? default(decimal)
274  };
275  break;
276 
277  case OrderType.TrailingStop:
278  order = new TrailingStopOrder
279  {
280  StopPrice = jObject["StopPrice"]?.Value<decimal>() ?? jObject["stopPrice"]?.Value<decimal>() ?? default(decimal),
281  TrailingAmount = jObject["TrailingAmount"]?.Value<decimal>() ?? jObject["trailingAmount"]?.Value<decimal>() ?? default(decimal),
282  TrailingAsPercentage = jObject["TrailingAsPercentage"]?.Value<bool>() ?? jObject["trailingAsPercentage"]?.Value<bool>() ?? default(bool)
283  };
284  break;
285 
286  case OrderType.LimitIfTouched:
287  order = new LimitIfTouchedOrder
288  {
289  LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal),
290  TriggerPrice = jObject["TriggerPrice"]?.Value<decimal>() ?? jObject["triggerPrice"]?.Value<decimal>() ?? default(decimal)
291  };
292  break;
293 
294  case OrderType.MarketOnOpen:
295  order = new MarketOnOpenOrder();
296  break;
297 
298  case OrderType.MarketOnClose:
299  order = new MarketOnCloseOrder();
300  break;
301 
302  case OrderType.OptionExercise:
303  order = new OptionExerciseOrder();
304  break;
305 
306  case OrderType.ComboMarket:
307  order = new ComboMarketOrder() { GroupOrderManager = DeserializeGroupOrderManager(jObject) };
308  break;
309 
310  case OrderType.ComboLimit:
311  order = new ComboLimitOrder() { GroupOrderManager = DeserializeGroupOrderManager(jObject) };
312  break;
313 
314  case OrderType.ComboLegLimit:
315  order = new ComboLegLimitOrder
316  {
317  GroupOrderManager = DeserializeGroupOrderManager(jObject),
318  LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal)
319  };
320  break;
321 
322  default:
323  throw new ArgumentOutOfRangeException();
324  }
325  return order;
326  }
327 
328  /// <summary>
329  /// Creates a Time In Force of the correct type
330  /// </summary>
331  private static TimeInForce CreateTimeInForce(JToken timeInForce, JObject jObject)
332  {
333  // for backward-compatibility support deserialization of old JSON format
334  if (timeInForce is JValue)
335  {
336  var value = timeInForce.Value<int>();
337 
338  switch (value)
339  {
340  case 0:
341  return TimeInForce.GoodTilCanceled;
342 
343  case 1:
344  return TimeInForce.Day;
345 
346  case 2:
347  var expiry = jObject["DurationValue"].Value<DateTime>();
348  return TimeInForce.GoodTilDate(expiry);
349 
350  default:
351  throw new Exception($"Unknown time in force value: {value}");
352  }
353  }
354 
355  // convert with TimeInForceJsonConverter
356  return timeInForce.ToObject<TimeInForce>();
357  }
358 
359  /// <summary>
360  /// Deserializes the GroupOrderManager from the JSON object
361  /// </summary>
362  private static GroupOrderManager DeserializeGroupOrderManager(JObject jObject)
363  {
364  var groupOrderManagerJObject = jObject["GroupOrderManager"] ?? jObject["groupOrderManager"];
365 
366  // this should never happen
367  if (groupOrderManagerJObject == null)
368  {
369  throw new ArgumentException("OrderJsonConverter.DeserializeGroupOrderManager(): JObject does not have a GroupOrderManager");
370  }
371 
372  var result = new GroupOrderManager(
373  groupOrderManagerJObject["Id"]?.Value<int>() ?? groupOrderManagerJObject["id"].Value<int>(),
374  groupOrderManagerJObject["Count"]?.Value<int>() ?? groupOrderManagerJObject["count"].Value<int>(),
375  groupOrderManagerJObject["Quantity"]?.Value<decimal>() ?? groupOrderManagerJObject["quantity"].Value<decimal>(),
376  groupOrderManagerJObject["LimitPrice"]?.Value<decimal>() ?? groupOrderManagerJObject["limitPrice"].Value<decimal>()
377  );
378 
379  foreach (var orderId in (groupOrderManagerJObject["OrderIds"]?.Values<int>() ?? groupOrderManagerJObject["orderIds"].Values<int>()))
380  {
381  result.OrderIds.Add(orderId);
382  }
383 
384  return result;
385  }
386  }
387 }