using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using QuantConnect.Orders; using QuantConnect.Packets; using QuantConnect.Securities; namespace QuantConnect.API { /// /// Custom JsonConverter for LiveResults data for live algorithms /// public class LiveAlgorithmResultsJsonConverter : JsonConverter { /// /// Gets a value indicating whether this can write JSON. /// /// /// true if this can write JSON; otherwise, false. /// public override bool CanWrite { get { return false; } } /// /// Writes the JSON representation of the object. /// /// The to write to.The value.The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException("The LiveAlgorithmResultsJsonConverter does not implement a WriteJson method."); } /// /// Determines whether this instance can convert the specified object type. /// /// Type of the object. /// /// true if this instance can convert the specified object type; otherwise, false. /// public override bool CanConvert(Type objectType) { return typeof(LiveAlgorithmResults).IsAssignableFrom(objectType); } /// /// Reads the JSON representation of the object. /// /// The to read from.Type of the object.The existing value of object being read.The calling serializer. /// /// The object value. /// public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var liveResults = CreateLiveResultsFromJObject(jObject); return liveResults; } /// /// Custom parsing of live results data /// /// Json representing LiveResults /// public static LiveAlgorithmResults CreateLiveResultsFromJObject(JObject jObject) { var liveAlgoResults = new LiveAlgorithmResults { Success = jObject["success"].Value() }; var success = jObject["success"].Value(); if (!success) { // Either there was an error in the running algrithm or the algorithm hasn't started liveAlgoResults.Errors = jObject.Last.Children().Select(error => error.ToString()).ToList(); return liveAlgoResults; } liveAlgoResults.Success = true; liveAlgoResults.LiveResults = new LiveResultsData { Resolution = (Resolution)Enum.Parse(typeof(Resolution), jObject["LiveResults"]["resolution"].Value(), true), Version = jObject["LiveResults"]["version"].Value() }; // Results json var results = jObject["LiveResults"]["results"]; // Deserialize charting data var charts = results["Charts"]; var chartDictionary = new Dictionary(); foreach (var chart in charts.Children()) { var newChart = new Chart(((JProperty) chart).Name) { Series = GetChartSeries(chart.First()["Series"]) }; chartDictionary.Add(newChart.Name, newChart); } // Live Results - At this time only that charting data can be returned from the api (9/30/2016) liveAlgoResults.LiveResults.Results = new LiveResult(chartDictionary, new Dictionary(), new Dictionary(), new Dictionary(), new CashBook(), new Dictionary(), new Dictionary() ); return liveAlgoResults; } /// /// Get series data for a specific chart /// /// Series data and properties for a chart /// Dictionary with the name of the series as the key and the Series itself as the value private static Dictionary GetChartSeries(JToken series) { var chartSeriesDict = new Dictionary(); foreach (var child in series.Children()) { var s = child.First(); var newSeries = new Series(((JProperty) child).Name) { SeriesType = (SeriesType) s["SeriesType"].Value(), Values = GetSeriesValues(s["Values"]) }; chartSeriesDict.Add(newSeries.Name, newSeries); } return chartSeriesDict; } /// /// Get x and y value pairs that represent series data /// /// json array of x, y value pairs /// List of ChartPoints private static List GetSeriesValues(JToken values) { var chartPoints = new List(); // Special ChartPoint that only represents time (only has x component) if (values.Children().Count() == 1) { var point = values.Children().First(); var x = point["x"]; chartPoints.Add(new ChartPoint((long)x, 0)); } // Typical series of values that is used for charting else { foreach (var point in values.Children()) { var x = point["x"]; var y = point["y"]; // this piece of code is why this entire custom serializer is necessary if (y != null && y.Type == JTokenType.Float) { chartPoints.Add(new ChartPoint((long)x, (decimal)y)); } else { chartPoints.Add(null); } } } return chartPoints; } } }