private Tick CreateDerivativeTick(QuoteStreamDefinition tsd, SecurityType derivativeType = SecurityType.Equity) { Symbol symbol = null; //TODO: fix this hackiness if (derivativeType == SecurityType.Option) { symbol = _symbolMapper.OptionNameResolver.FirstOrDefault(x => x.Value == tsd.Symbol).Key; if (symbol == null) { symbol = _subscriptions.FirstOrDefault(x => x.Key.Value.StartsWith("?") && x.Key.ID.Symbol == tsd.Symbol && x.Key.SecurityType == SecurityType.Option).Key; } } else { symbol = _subscriptions.FirstOrDefault(x => x.Key.ID.Symbol == tsd.Symbol && x.Key.SecurityType == derivativeType).Key; } // Not subscribed to this symbol. if (symbol == null) { //Log.Trace("TradeStation.DataQueueHandler.Stream(): Not subscribed to symbol " + tsd.Symbol); return(null); } //this is bad/useless data //if (tsd.TradeTime == DateTime.MinValue) return null; //TODO: hack fix tsd.TradeTime = GetRealTimeTickTime(symbol); var tick = new Tick { Exchange = tsd.Exchange, TickType = symbol.ID.SecurityType == SecurityType.Option ? TickType.Quote : TickType.Trade, Quantity = (int)tsd.Volume, Time = tsd.TradeTime, EndTime = tsd.TradeTime, Symbol = symbol, //DataType = MarketDataType.Tick, Suspicious = false, Value = (decimal)tsd.Last, AskPrice = (decimal)tsd.Ask, AskSize = (decimal)tsd.AskSize, BidPrice = (decimal)tsd.Bid, BidSize = (decimal)tsd.BidSize }; /* * if (tick.TickType == TickType.Quote) * { * Console.WriteLine("got option quote: {0}\t= {1}, {2}", tick.Symbol.ToString(), tick.Value, tick.Time); * } */ return(tick); }
private void mergeQuote(QuoteStreamDefinition source, QuoteStreamDefinition target) { //we only care about the things needed to create a valid 'Tick' object target.Exchange = target.Exchange != null ? target.Exchange : source.Exchange; target.Volume = target.Volume != null ? target.Volume : 0; target.TradeTime = target.TradeTime > DateTime.MinValue ? target.TradeTime : source.TradeTime; target.Last = target.Last != null ? target.Last : source.Last; target.Ask = target.Ask != null ? target.Ask : source.Ask; target.AskSize = target.AskSize != null ? target.AskSize : source.AskSize; target.Bid = target.Bid != null ? target.Bid : source.Bid; target.BidSize = target.BidSize != null ? target.BidSize : source.BidSize; }
/// <summary> /// Connect to tradier API strea: /// </summary> /// <param name="symbols">symbol list</param> /// <returns></returns> private IEnumerable <QuoteStreamDefinition> Stream() { if (_tradestationStream != null) { //only 1 is allowed at a time _tradestationStream.Close(); } //Tradestation will send a full quote first, then only changes. We need to keep track of the full one, // and merge in the changed one. var activeQuotes = new Dictionary <string, QuoteStreamDefinition>(); //Gather together all the symbols we want to subscribe to var symbols = new HashSet <string>(); foreach (var sub in _subscriptions) { if (sub.Key.Value.StartsWith("?")) { //resolve derivative symbols.Add(sub.Key.ID.Symbol); } else if (sub.Key.SecurityType == SecurityType.Option) { if (_symbolMapper.OptionNameResolver.ContainsKey(sub.Key)) { symbols.Add(_symbolMapper.OptionNameResolver[sub.Key]); } else { Log.Error("No option symbol was resolved for " + sub.Key.Value); } } else { symbols.Add(sub.Value); } } var symbolJoined = String.Join(",", symbols); Log.Trace("TradeStation.Stream(): Creating new session, Reading Stream... (" + symbols.Count + " tickers)", true); HttpWebRequest request; request = (HttpWebRequest)WebRequest.Create(String.Format("{0}/stream/quote/changes/{1}?access_token={2}", _tradeStationClient.BaseUrl, symbolJoined, _accessToken)); request.Timeout = 30 * 1000; request.Accept = "application/vnd.tradestation.streams+json"; //Get response as a stream: try { var response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode != HttpStatusCode.OK) { Log.Trace("TradeStation.DataQueueHandler.Stream(): Bad request status " + response.StatusCode + "! Disconnecting from stream..."); _isConnected = false; yield break; } _tradestationStream = response.GetResponseStream(); if (_tradestationStream == null) { Log.Error("TradeStation.DataQueueHandler.Stream(): Null stream error!"); yield break; } } catch (Exception ex) { Log.Error("TradeStation.DataQueueHandler.Stream(): Error establishing connection: " + ex.Message); yield break; } using (_tradestationStream) using (var sr = new StreamReader(_tradestationStream)) using (var jsonReader = new JsonTextReader(sr)) { jsonReader.SupportMultipleContent = true; // keep going until stream gets closed while (!_refresh) { JToken token = null; try { if (!_tradestationStream.CanRead) { yield break; //stream closed down, exit normally } //Read the jsonSocket in a safe manner: might close and so need handlers, but can't put handlers around a yield. jsonReader.Read(); if (jsonReader.TokenType != JsonToken.StartObject) { Log.Debug("TradeStation.DataQueueHandler.Stream(): token parse error"); continue; //bad json or we're parsing in the wrong place somehow, just move along... } token = JToken.Load(jsonReader); } catch (Exception err) { Log.Trace("TradeStation.DataQueueHandler.Stream(): Stream read error: " + err.Message); } if (token == null) { // if we couldn't get a successful read, abort this session yield break; } //after the first read, we set a high timeout on the socket, the server can keep it open as long as it wants. //... but if nothing comes in for a few minutes, might as well restart the stream. _tradestationStream.ReadTimeout = 3 * 60 * 1000; //3mins //now deserialize it for processing /* * Anonymous3 z = null; * try * { * z = token.ToObject<Anonymous3>(); * if (z!=null) * { * Log.Trace("got snapshot from stream"); * } * } * catch (Exception err) * { * // Do nothing for now. Can come back later to fix. Errors are from Tradier not properly json encoding values E.g. "NaN" string. * Log.Trace("TradeStation.DataQueueHandler.Stream(): z json deserialization error: " + err.Message); * } */ QuoteStreamDefinition tsd = null; try { tsd = token.ToObject <QuoteStreamDefinition>(); if (tsd != null) { Log.Debug("TradeStation.DataQueueHandler.Stream(): got quote: " + token.ToString()); } } catch (Exception err) { // Do nothing for now. Can come back later to fix. Errors are from Tradier not properly json encoding values E.g. "NaN" string. Log.Error("TradeStation.DataQueueHandler.Stream(): json deserialization error: " + err.Message); } // don't yield garbage, just wait for the next one if (tsd != null) { if (!activeQuotes.ContainsKey(tsd.Symbol)) { activeQuotes[tsd.Symbol] = tsd; } else { mergeQuote(activeQuotes[tsd.Symbol], tsd); } yield return(tsd); } //no need to rail the cpu doing this Thread.Sleep(10); } } }
/// <summary> /// Create a tick from the tradier stream data: /// </summary> /// <param name="tsd">Tradier stream data obejct</param> /// <returns>LEAN Tick object</returns> private Tick CreateTick(QuoteStreamDefinition tsd) { return(CreateDerivativeTick(tsd, SecurityType.Equity)); }